///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// TetGen                                                                    //
//                                                                           //
// A Quality Tetrahedral Mesh Generator and 3D Delaunay Triangulator         //
//                                                                           //
// Version 1.4                                                               //
// September 6, December 13, 2010                                            //
// January 19, April 15, 2011
//                                                                           //
// Copyright (C) 2002--2011                                                  //
// Hang Si                                                                   //
// Research Group: Numerical Mathematics and Scientific Computing            //
// Weierstrass Institute for Applied Analysis and Stochastics (WIAS)         //
// Mohrenstr. 39, 10117 Berlin, Germany                                      //
// si@wias-berlin.de                                                         //
//                                                                           //
// TetGen is freely available through the website: http://tetgen.berlios.de. //
//   It may be copied, modified, and redistributed for non-commercial use.   //
//   Please consult the file LICENSE for the detailed copyright notices.     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// tetgen.cxx                                                                //
//                                                                           //
// The TetGen library and program.                                           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

#include "tetgen.h"

//// io_cxx ///////////////////////////////////////////////////////////////////
////                                                                       ////
////                                                                       ////

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// load_node_call()    Read a list of points from a file.                    //
//                                                                           //
// It is a support routine for routines: 'load_nodes()', 'load_poly()', and  //
// 'load_tetmesh()'.  'infile' is the file handle contains the node list. It //
// may point to a .node, or .poly or .smesh file.  'markers' indicates each  //
// node contains an additional marker (integer) or not. 'infilename' is the  //
// name of the file being read,  it is only used in error messages.          //
//                                                                           //
// The 'firstnumber' (0 or 1) is automatically determined by the number of   //
// the first index of the first point.                                       //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenio::load_node_call(FILE* infile, int markers, char* infilename)
{
  char inputline[INPUTLINESIZE];
  char *stringptr;
  REAL x, y, z, attrib;
  int firstnode, currentmarker;
  int index, attribindex;
  int i, j;

  // Initialize 'pointlist', 'pointattributelist', and 'pointmarkerlist'.
  pointlist = new REAL[numberofpoints * 3];
  if (pointlist == (REAL *) NULL) {
    terminatetetgen(1);
  }
  if (numberofpointattributes > 0) {
    pointattributelist = new REAL[numberofpoints * numberofpointattributes];
    if (pointattributelist == (REAL *) NULL) {
      terminatetetgen(1);
    }
  }
  if (markers) {
    pointmarkerlist = new int[numberofpoints];
    if (pointmarkerlist == (int *) NULL) {
      terminatetetgen(1);
    }
  }

  // Read the point section.
  index = 0;
  attribindex = 0;
  for (i = 0; i < numberofpoints; i++) {
    stringptr = readnumberline(inputline, infile, infilename);
    if (useindex) {
      if (i == 0) {
        firstnode = (int) strtol (stringptr, &stringptr, 0);
        if ((firstnode == 0) || (firstnode == 1)) {
          firstnumber = firstnode;
        }
      }
      stringptr = findnextnumber(stringptr);
    } // if (useindex)
    if (*stringptr == '\0') {
      //printf("Error:  Point %d has no x coordinate.\n", firstnumber + i);
      break;
    }
    x = (REAL) strtod(stringptr, &stringptr);
    stringptr = findnextnumber(stringptr);
    if (*stringptr == '\0') {
      //printf("Error:  Point %d has no y coordinate.\n", firstnumber + i);
      break;
    }
    y = (REAL) strtod(stringptr, &stringptr);
    if (mesh_dim == 3) {
      stringptr = findnextnumber(stringptr);
      if (*stringptr == '\0') {
        //printf("Error:  Point %d has no z coordinate.\n", firstnumber + i);
        break;
      }
      z = (REAL) strtod(stringptr, &stringptr);
    } else {
      z = 0.0; // mesh_dim == 2;
    }
    pointlist[index++] = x;
    pointlist[index++] = y;
    pointlist[index++] = z;
    // Read the point attributes.
    for (j = 0; j < numberofpointattributes; j++) {
      stringptr = findnextnumber(stringptr);
      if (*stringptr == '\0') {
        attrib = 0.0;
      } else {
        attrib = (REAL) strtod(stringptr, &stringptr);
      }
      pointattributelist[attribindex++] = attrib;
    }
    if (markers) {
      // Read a point marker.
      stringptr = findnextnumber(stringptr);
      if (*stringptr == '\0') {
        currentmarker = 0;
      } else {
        currentmarker = (int) strtol (stringptr, &stringptr, 0);
      }
      pointmarkerlist[i] = currentmarker;
    }
  }
  if (i < numberofpoints) {
    // Failed to read points due to some error.
    delete [] pointlist;
    pointlist = (REAL *) NULL;
    if (markers) {
      delete [] pointmarkerlist;
      pointmarkerlist = (int *) NULL;
    }
    if (numberofpointattributes > 0) {
      delete [] pointattributelist;
      pointattributelist = (REAL *) NULL;
    }
    numberofpoints = 0;
    return false;
  }
  return true;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// load_node()    Load a list of points from a .node file.                   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenio::load_node(char* filebasename)
{
  FILE *infile;
  char innodefilename[FILENAMESIZE];
  char inputline[INPUTLINESIZE];
  char *stringptr;
  bool okflag;
  int markers;

  // Assembling the actual file names we want to open.
  strcpy(innodefilename, filebasename);
  strcat(innodefilename, ".node");

  // Try to open a .node file.
  infile = fopen(innodefilename, "r");
  if (infile == (FILE *) NULL) {
    //printf("File I/O Error:  Cannot access file %s.\n", innodefilename);
    return false;
  }
  //printf("Opening %s.\n", innodefilename);  
  // Read the first line of the file.
  stringptr = readnumberline(inputline, infile, innodefilename);
  // Does this file contain an index colume?
  stringptr = strstr(inputline, "rbox");
  if (stringptr == NULL) {
    // Read number of points, number of dimensions, number of point
    //   attributes, and number of boundary markers. 
    stringptr = inputline;
    numberofpoints = (int) strtol (stringptr, &stringptr, 0);
    stringptr = findnextnumber(stringptr);
    if (*stringptr == '\0') {
      mesh_dim = 3;
    } else {
      mesh_dim = (int) strtol (stringptr, &stringptr, 0);
    }
    stringptr = findnextnumber(stringptr);
    if (*stringptr == '\0') {
      numberofpointattributes = 0;
    } else {
      numberofpointattributes = (int) strtol (stringptr, &stringptr, 0);
    }
    stringptr = findnextnumber(stringptr);
    if (*stringptr == '\0') {
      markers = 0;
    } else {
      markers = (int) strtol (stringptr, &stringptr, 0);
    }
  } else {
    // It is a rbox (qhull) input file.
    stringptr = inputline;
    // Get the dimension.
    mesh_dim = (int) strtol (stringptr, &stringptr, 0);
    // Get the number of points.
    stringptr = readnumberline(inputline, infile, innodefilename);
    numberofpoints = (int) strtol (stringptr, &stringptr, 0);
    // There is no index column.
    useindex = 0;
  }

  // Load the list of nodes.
  okflag = load_node_call(infile, markers, innodefilename);

  fclose(infile);
  return okflag;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// load_var()    Load constraints applied on facets, segments, and nodes     //
//               from a .var file.                                           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenio::load_var(char* filebasename)
{
  FILE *infile;
  char varfilename[FILENAMESIZE];
  char inputline[INPUTLINESIZE];
  char *stringptr;
  int index;
  int i;

  // Variant constraints are saved in file "filename.var".
  strcpy(varfilename, filebasename);
  strcat(varfilename, ".var");
  infile = fopen(varfilename, "r");
  if (infile != (FILE *) NULL) {
    //printf("Opening %s.\n", varfilename);
  } else {
    // No such file. Ignore it without a message.
    return false;
  }

  // Read the facet constraint section.
  stringptr = readnumberline(inputline, infile, varfilename);
  if (*stringptr != '\0') {
    numberoffacetconstraints = (int) strtol (stringptr, &stringptr, 0);
  } else {
    numberoffacetconstraints = 0;
  }
  if (numberoffacetconstraints > 0) {
    // Initialize 'facetconstraintlist'.
    facetconstraintlist = new REAL[numberoffacetconstraints * 2];
    index = 0;
    for (i = 0; i < numberoffacetconstraints; i++) {
      stringptr = readnumberline(inputline, infile, varfilename);
      stringptr = findnextnumber(stringptr);
      if (*stringptr == '\0') {
        //printf("Error:  facet constraint %d has no facet marker.\n",
              // firstnumber + i);
        break;
      } else {
        facetconstraintlist[index++] = (REAL) strtod(stringptr, &stringptr);
      }
      stringptr = findnextnumber(stringptr);
      if (*stringptr == '\0') {
        //printf("Error:  facet constraint %d has no maximum area bound.\n",
              // firstnumber + i);
        break;
      } else {
        facetconstraintlist[index++] = (REAL) strtod(stringptr, &stringptr);
      }
    }
    if (i < numberoffacetconstraints) {
      // This must be caused by an error.
      fclose(infile);
      return false;
    }
  }

  // Read the segment constraint section.
  stringptr = readnumberline(inputline, infile, varfilename);
  if (*stringptr != '\0') {
    numberofsegmentconstraints = (int) strtol (stringptr, &stringptr, 0);
  } else {
    numberofsegmentconstraints = 0;
  }
  if (numberofsegmentconstraints > 0) {
    // Initialize 'segmentconstraintlist'.
    segmentconstraintlist = new REAL[numberofsegmentconstraints * 3];
    index = 0;
    for (i = 0; i < numberofsegmentconstraints; i++) {
      stringptr = readnumberline(inputline, infile, varfilename);
      stringptr = findnextnumber(stringptr);
      if (*stringptr == '\0') {
        //printf("Error:  segment constraint %d has no frist endpoint.\n",
               //firstnumber + i);
        break;
      } else {
        segmentconstraintlist[index++] = (REAL) strtod(stringptr, &stringptr);
      }
      stringptr = findnextnumber(stringptr);
      if (*stringptr == '\0') {
        //printf("Error:  segment constraint %d has no second endpoint.\n",
             //  firstnumber + i);
        break;
      } else {
        segmentconstraintlist[index++] = (REAL) strtod(stringptr, &stringptr);
      }
      stringptr = findnextnumber(stringptr);
      if (*stringptr == '\0') {
        //printf("Error:  segment constraint %d has no maximum length bound.\n",
             //  firstnumber + i);
        break;
      } else {
        segmentconstraintlist[index++] = (REAL) strtod(stringptr, &stringptr);
      }
    }
    if (i < numberofsegmentconstraints) {
      // This must be caused by an error.
      fclose(infile);
      return false;
    }
  }

  fclose(infile);
  return true;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// load_mtr()    Load a size specification map from a .mtr file.             //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenio::load_mtr(char* filebasename)
{
  FILE *infile;
  char mtrfilename[FILENAMESIZE];
  char inputline[INPUTLINESIZE];
  char *stringptr;
  REAL mtr;
  int mtrindex;
  int i, j;

  strcpy(mtrfilename, filebasename);
  strcat(mtrfilename, ".mtr");
  infile = fopen(mtrfilename, "r");
  if (infile != (FILE *) NULL) {
    //printf("Opening %s.\n", mtrfilename);
  } else {
    // No such file. Return.
    return false;
  }

  // Read number of points, number of columns (1, 3, or 6).
  stringptr = readnumberline(inputline, infile, mtrfilename);
  stringptr = findnextnumber(stringptr); // Skip number of points.
  if (*stringptr != '\0') {
    numberofpointmtrs = (int) strtol (stringptr, &stringptr, 0);
  }
  if (numberofpointmtrs == 0) {
    // Column number doesn't match. Set a default number (1).
    numberofpointmtrs = 1;
  }

  // Allocate space for pointmtrlist.
  pointmtrlist = new REAL[numberofpoints * numberofpointmtrs];
  if (pointmtrlist == (REAL *) NULL) {
    terminatetetgen(1);
  }
  mtrindex = 0;
  for (i = 0; i < numberofpoints; i++) {
    // Read metrics.
    stringptr = readnumberline(inputline, infile, mtrfilename);
    for (j = 0; j < numberofpointmtrs; j++) {
      if (*stringptr == '\0') {
        //printf("Error:  Metric %d is missing value #%d in %s.\n",
              // i + firstnumber, j + 1, mtrfilename);
        //terminatetetgen(1);
		  return;
      }
      mtr = (REAL) strtod(stringptr, &stringptr);
      pointmtrlist[mtrindex++] = mtr;
      stringptr = findnextnumber(stringptr);
    }
  }

  fclose(infile);
  return true;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// load_poly()    Load a PL complex from a .poly or a .smesh file.           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenio::load_poly(char* filebasename)
{
  FILE *infile, *polyfile;
  char innodefilename[FILENAMESIZE];
  char inpolyfilename[FILENAMESIZE];
  char insmeshfilename[FILENAMESIZE];
  char inputline[INPUTLINESIZE];
  char *stringptr, *infilename;
  int smesh, markers, currentmarker;
  int readnodefile, index;
  int i, j, k;

  // Assembling the actual file names we want to open.
  strcpy(innodefilename, filebasename);
  strcpy(inpolyfilename, filebasename);
  strcpy(insmeshfilename, filebasename);
  strcat(innodefilename, ".node");
  strcat(inpolyfilename, ".poly");
  strcat(insmeshfilename, ".smesh");

  // First assume it is a .poly file.
  smesh = 0;
  // Try to open a .poly file.
  polyfile = fopen(inpolyfilename, "r");
  if (polyfile == (FILE *) NULL) {
    // .poly doesn't exist! Try to open a .smesh file.
    polyfile = fopen(insmeshfilename, "r");
    if (polyfile == (FILE *) NULL) {
      //printf("File I/O Error:  Cannot access file %s and %s.\n",
             //inpolyfilename, insmeshfilename);
      return false;
    } else {
      //printf("Opening %s.\n", insmeshfilename);
    }
    smesh = 1;
  } else {
    //printf("Opening %s.\n", inpolyfilename);
  }
  // Initialize the default values.
  mesh_dim = 3;  // Three-dimemsional accoordinates.
  numberofpointattributes = 0;  // no point attribute.
  markers = 0;  // no boundary marker.
  // Read number of points, number of dimensions, number of point
  //   attributes, and number of boundary markers.
  stringptr = readnumberline(inputline, polyfile, inpolyfilename);
  numberofpoints = (int) strtol (stringptr, &stringptr, 0);
  stringptr = findnextnumber(stringptr);
  if (*stringptr != '\0') {
    mesh_dim = (int) strtol (stringptr, &stringptr, 0);      
  }
  stringptr = findnextnumber(stringptr);
  if (*stringptr != '\0') {
    numberofpointattributes = (int) strtol (stringptr, &stringptr, 0);
  }
  stringptr = findnextnumber(stringptr);
  if (*stringptr != '\0') {
    markers = (int) strtol (stringptr, &stringptr, 0);
  }
  if (numberofpoints > 0) {
    readnodefile = 0;
    if (smesh) {
      infilename = insmeshfilename;
    } else {
      infilename = inpolyfilename;
    } 
    infile = polyfile;
  } else {
    // If the .poly or .smesh file claims there are zero points, that
    //   means the points should be read from a separate .node file.
    readnodefile = 1;
    infilename = innodefilename;
  }

  if (readnodefile) {
    // Read the points from the .node file.
    //printf("Opening %s.\n", innodefilename);
    infile = fopen(innodefilename, "r");
    if (infile == (FILE *) NULL) {
      //printf("File I/O Error:  Cannot access file %s.\n", innodefilename);
      return false;
    }
    // Initialize the default values.
    mesh_dim = 3;  // Three-dimemsional accoordinates.
    numberofpointattributes = 0;  // no point attribute.
    markers = 0;  // no boundary marker.
    // Read number of points, number of dimensions, number of point
    //   attributes, and number of boundary markers.
    stringptr = readnumberline(inputline, infile, innodefilename);
    numberofpoints = (int) strtol (stringptr, &stringptr, 0);
    stringptr = findnextnumber(stringptr);
    if (*stringptr != '\0') {
      mesh_dim = (int) strtol (stringptr, &stringptr, 0);
    }
    stringptr = findnextnumber(stringptr);
    if (*stringptr != '\0') {
      numberofpointattributes = (int) strtol (stringptr, &stringptr, 0);
    }
    stringptr = findnextnumber(stringptr);
    if (*stringptr != '\0') {
      markers = (int) strtol (stringptr, &stringptr, 0);
    }
  }

  if ((mesh_dim != 3) && (mesh_dim != 2)) {
    //printf("Input error:  TetGen only works for 2D & 3D point sets.\n");
    fclose(infile);
    return false;
  }
  if (numberofpoints < (mesh_dim + 1)) {
    //printf("Input error:  TetGen needs at least %d points.\n", mesh_dim + 1);
    fclose(infile);
    return false;
  }

  // Load the list of nodes.
  if (!load_node_call(infile, markers, infilename)) {
    fclose(infile);
    return false;
  }

  if (readnodefile) {
    fclose(infile);
  }

  facet *f;
  polygon *p;

  if (mesh_dim == 3) {

    // Read number of facets and number of boundary markers.
    stringptr = readnumberline(inputline, polyfile, inpolyfilename);
    numberoffacets = (int) strtol (stringptr, &stringptr, 0);
    if (numberoffacets <= 0) {
      // No facet list, return.
      fclose(polyfile);
      return true;
    }
    stringptr = findnextnumber(stringptr);
    if (*stringptr == '\0') {
      markers = 0;  // no boundary marker.
    } else {
      markers = (int) strtol (stringptr, &stringptr, 0);
    }

    // Initialize the 'facetlist', 'facetmarkerlist'.
    facetlist = new facet[numberoffacets];
    if (markers == 1) {
      facetmarkerlist = new int[numberoffacets];
    }

    // Read data into 'facetlist', 'facetmarkerlist'.
    if (smesh == 0) {
      // Facets are in .poly file format.
      for (i = 1; i <= numberoffacets; i++) {
        f = &(facetlist[i - 1]);
        init(f);
        f->numberofholes = 0;
        currentmarker = 0;
        // Read number of polygons, number of holes, and a boundary marker.
        stringptr = readnumberline(inputline, polyfile, inpolyfilename);
        f->numberofpolygons = (int) strtol (stringptr, &stringptr, 0);
        stringptr = findnextnumber(stringptr);
        if (*stringptr != '\0') {
          f->numberofholes = (int) strtol (stringptr, &stringptr, 0);
          if (markers == 1) {
            stringptr = findnextnumber(stringptr);
            if (*stringptr != '\0') {
              currentmarker = (int) strtol(stringptr, &stringptr, 0);
            }
          }
        }
        // Initialize facetmarker if it needs.
        if (markers == 1) {
          facetmarkerlist[i - 1] = currentmarker; 
        }
        // Each facet should has at least one polygon.
        if (f->numberofpolygons <= 0) {
          //printf("Error:  Wrong number of polygon in %d facet.\n", i);
          break; 
        }
        // Initialize the 'f->polygonlist'.
        f->polygonlist = new polygon[f->numberofpolygons];
        // Go through all polygons, read in their vertices.
        for (j = 1; j <= f->numberofpolygons; j++) {
          p = &(f->polygonlist[j - 1]);
          init(p);
          // Read number of vertices of this polygon.
          stringptr = readnumberline(inputline, polyfile, inpolyfilename);
          p->numberofvertices = (int) strtol(stringptr, &stringptr, 0);
          if (p->numberofvertices < 1) {
            //printf("Error:  Wrong polygon %d in facet %d\n", j, i);
            break;
          }
          // Initialize 'p->vertexlist'.
          p->vertexlist = new int[p->numberofvertices];
          // Read all vertices of this polygon.
          for (k = 1; k <= p->numberofvertices; k++) {
            stringptr = findnextnumber(stringptr);
            if (*stringptr == '\0') {
              // Try to load another non-empty line and continue to read the
              //   rest of vertices.
              stringptr = readnumberline(inputline, polyfile, inpolyfilename);
              if (*stringptr == '\0') {
                //printf("Error: Missing %d endpoints of polygon %d in facet %d",
                     //  p->numberofvertices - k, j, i);
                break;
              }
            }
            p->vertexlist[k - 1] = (int) strtol (stringptr, &stringptr, 0);
          }
        } 
        if (j <= f->numberofpolygons) {
          // This must be caused by an error. However, there're j - 1
          //   polygons have been read. Reset the 'f->numberofpolygon'.
          if (j == 1) {
            // This is the first polygon.
            delete [] f->polygonlist;
          }
          f->numberofpolygons = j - 1;
          // No hole will be read even it exists.
          f->numberofholes = 0;
          break;
        }
        // If this facet has hole pints defined, read them.
        if (f->numberofholes > 0) {
          // Initialize 'f->holelist'.
          f->holelist = new REAL[f->numberofholes * 3];
          // Read the holes' coordinates.
          index = 0;
          for (j = 1; j <= f->numberofholes; j++) {
            stringptr = readnumberline(inputline, polyfile, inpolyfilename);
            for (k = 1; k <= 3; k++) {
              stringptr = findnextnumber(stringptr);
              if (*stringptr == '\0') {
                //printf("Error:  Hole %d in facet %d has no coordinates", j, i);
                break;
              }
              f->holelist[index++] = (REAL) strtod (stringptr, &stringptr);
            }
            if (k <= 3) {
              // This must be caused by an error.
              break;
            }
          }
          if (j <= f->numberofholes) {
            // This must be caused by an error.
            break;
          }
        }
      }
      if (i <= numberoffacets) {
        // This must be caused by an error.
        numberoffacets = i - 1;
        fclose(polyfile);
        return false;
      }
    } else { // poly == 0
      // Read the facets from a .smesh file.
      for (i = 1; i <= numberoffacets; i++) {
        f = &(facetlist[i - 1]);
        init(f);
        // Initialize 'f->facetlist'. In a .smesh file, each facetlist only
        //   contains exactly one polygon, no hole.
        f->numberofpolygons = 1;
        f->polygonlist = new polygon[f->numberofpolygons];
        p = &(f->polygonlist[0]);
        init(p);
        // Read number of vertices of this polygon.
        stringptr = readnumberline(inputline, polyfile, insmeshfilename);
        p->numberofvertices = (int) strtol (stringptr, &stringptr, 0);
        if (p->numberofvertices < 1) {
          //printf("Error:  Wrong number of vertex in facet %d\n", i);
          break;
        }
        // Initialize 'p->vertexlist'.
        p->vertexlist = new int[p->numberofvertices];
        for (k = 1; k <= p->numberofvertices; k++) {
          stringptr = findnextnumber(stringptr);
          if (*stringptr == '\0') {
            // Try to load another non-empty line and continue to read the
            //   rest of vertices.
            stringptr = readnumberline(inputline, polyfile, inpolyfilename);
            if (*stringptr == '\0') {
              //printf("Error:  Missing %d endpoints in facet %d",
                  //   p->numberofvertices - k, i);
              break;
            }
          }
          p->vertexlist[k - 1] = (int) strtol (stringptr, &stringptr, 0);
        }
        if (k <= p->numberofvertices) {
          // This must be caused by an error.
          break;
        }
        // Read facet's boundary marker at last.
        if (markers == 1) {
          stringptr = findnextnumber(stringptr);
          if (*stringptr == '\0') {
            currentmarker = 0;
          } else {
            currentmarker = (int) strtol(stringptr, &stringptr, 0);
          }
          facetmarkerlist[i - 1] = currentmarker;
        }
      }
      if (i <= numberoffacets) {
        // This must be caused by an error.
        numberoffacets = i - 1;
        fclose(polyfile);
        return false;
      }
    }

    // Read the hole section.
    stringptr = readnumberline(inputline, polyfile, inpolyfilename);
    if (*stringptr != '\0') {
      numberofholes = (int) strtol (stringptr, &stringptr, 0);
    } else {
      numberofholes = 0;
    }
    if (numberofholes > 0) {
      // Initialize 'holelist'.
      holelist = new REAL[numberofholes * 3];
      for (i = 0; i < 3 * numberofholes; i += 3) {
        stringptr = readnumberline(inputline, polyfile, inpolyfilename);
        stringptr = findnextnumber(stringptr);
        if (*stringptr == '\0') {
          //printf("Error:  Hole %d has no x coord.\n", firstnumber + (i / 3));
          break;
        } else {
          holelist[i] = (REAL) strtod(stringptr, &stringptr);
        }
        stringptr = findnextnumber(stringptr);
        if (*stringptr == '\0') {
          //printf("Error:  Hole %d has no y coord.\n", firstnumber + (i / 3));
          break;
        } else {
          holelist[i + 1] = (REAL) strtod(stringptr, &stringptr);
        }
        stringptr = findnextnumber(stringptr);
        if (*stringptr == '\0') {
          //printf("Error:  Hole %d has no z coord.\n", firstnumber + (i / 3));
          break;
        } else {
          holelist[i + 2] = (REAL) strtod(stringptr, &stringptr);
        }
      }
      if (i < 3 * numberofholes) {
        // This must be caused by an error.
        fclose(polyfile);
        return false;
      }
    }

    // Read the region section.  The 'region' section is optional, if we
    //   don't reach the end-of-file, try read it in.
    stringptr = readnumberline(inputline, polyfile, NULL);
    if (stringptr != (char *) NULL && *stringptr != '\0') {
      numberofregions = (int) strtol (stringptr, &stringptr, 0);
    } else {
      numberofregions = 0;
    }
    if (numberofregions > 0) {
      // Initialize 'regionlist'.
      regionlist = new REAL[numberofregions * 5];
      index = 0;
      for (i = 0; i < numberofregions; i++) {
        stringptr = readnumberline(inputline, polyfile, inpolyfilename);
        stringptr = findnextnumber(stringptr);
        if (*stringptr == '\0') {
          //printf("Error:  Region %d has no x coordinate.\n", firstnumber + i);
          break;
        } else {
          regionlist[index++] = (REAL) strtod(stringptr, &stringptr);
        }
        stringptr = findnextnumber(stringptr);
        if (*stringptr == '\0') {
          //printf("Error:  Region %d has no y coordinate.\n", firstnumber + i);
          break;
        } else {
          regionlist[index++] = (REAL) strtod(stringptr, &stringptr);
        }
        stringptr = findnextnumber(stringptr);
        if (*stringptr == '\0') {
          //printf("Error:  Region %d has no z coordinate.\n", firstnumber + i);
          break;
        } else {
          regionlist[index++] = (REAL) strtod(stringptr, &stringptr);
        }
        stringptr = findnextnumber(stringptr);
        if (*stringptr == '\0') {
          //printf("Error:  Region %d has no region attrib.\n", firstnumber + i);
          break;
        } else {
          regionlist[index++] = (REAL) strtod(stringptr, &stringptr);
        }
        stringptr = findnextnumber(stringptr);
        if (*stringptr == '\0') {
          regionlist[index] = regionlist[index - 1];
        } else {
          regionlist[index] = (REAL) strtod(stringptr, &stringptr);
        }
        index++;
      }
      if (i < numberofregions) {
        // This must be caused by an error.
        fclose(polyfile);
        return false;
      }
    }

  } else {

    // Read a PSLG from Triangle's poly file.
    //assert(mesh_dim == 2);
    // A PSLG is a facet of a PLC.
    numberoffacets = 1;
    // Initialize the 'facetlist'.
    facetlist = new facet[numberoffacets];
    facetmarkerlist = (int *) NULL; // No facet markers.
    f = &(facetlist[0]);
    init(f);
    // Read number of segments.
    stringptr = readnumberline(inputline, polyfile, inpolyfilename);
    // Segments are degenerate polygons.
    f->numberofpolygons = (int) strtol (stringptr, &stringptr, 0);
    if (f->numberofpolygons > 0) {
      f->polygonlist = new polygon[f->numberofpolygons];
    }
    // Go through all segments, read in their vertices.
    for (j = 0; j < f->numberofpolygons; j++) {
      p = &(f->polygonlist[j]);
      init(p);
      // Read in a segment.
      stringptr = readnumberline(inputline, polyfile, inpolyfilename);
      stringptr = findnextnumber(stringptr); // Skip its index.
      p->numberofvertices = 2; // A segment always has two vertices.
      p->vertexlist = new int[p->numberofvertices];
      p->vertexlist[0] = (int) strtol (stringptr, &stringptr, 0);
      stringptr = findnextnumber(stringptr);
      p->vertexlist[1] = (int) strtol (stringptr, &stringptr, 0);
    }
    // Read number of holes.
    stringptr = readnumberline(inputline, polyfile, inpolyfilename);
    f->numberofholes = (int) strtol (stringptr, &stringptr, 0);
    if (f->numberofholes > 0) {
      // Initialize 'f->holelist'.
      f->holelist = new REAL[f->numberofholes * 3];
      // Read the holes' coordinates.
      for (j = 0; j < f->numberofholes; j++) {
        // Read a 2D hole point.
        stringptr = readnumberline(inputline, polyfile, inpolyfilename);
        stringptr = findnextnumber(stringptr); // Skip its index.
        f->holelist[j * 3] = (REAL) strtod (stringptr, &stringptr);
        stringptr = findnextnumber(stringptr);
        f->holelist[j * 3 + 1] = (REAL) strtod (stringptr, &stringptr);
        f->holelist[j * 3 + 2] = 0.0; // The z-coord.
      }
    }
    // The regions are skipped.

  }

  // End of reading poly/smesh file.
  fclose(polyfile);

  // Try to load a .var file if it exists.
  load_var(filebasename);

  // Try to load a .mtr file if it exists.
  load_mtr(filebasename);

  return true;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// load_off()    Load a polyhedron from a .off file.                         //
//                                                                           //
// The .off format is one of file formats of the Geomview, an interactive    //
// program for viewing and manipulating geometric objects.  More information //
// is available form: http://www.geomview.org.                               //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenio::load_off(char* filebasename)
{
  FILE *fp;
  tetgenio::facet *f;
  tetgenio::polygon *p;
  char infilename[FILENAMESIZE];
  char buffer[INPUTLINESIZE];
  char *bufferp;
  double *coord;
  int nverts = 0, iverts = 0;
  int nfaces = 0, ifaces = 0;
  int nedges = 0;
  int line_count = 0, i;

  strncpy(infilename, filebasename, 1024 - 1);
  infilename[FILENAMESIZE - 1] = '\0';
  if (infilename[0] == '\0') {
    //printf("Error:  No filename.\n");
    return false;
  }
  if (strcmp(&infilename[strlen(infilename) - 4], ".off") != 0) {
    strcat(infilename, ".off");
  }

  if (!(fp = fopen(infilename, "r"))) {
    //printf("File I/O Error:  Unable to open file %s\n", infilename);
    return false;
  }
  //printf("Opening %s.\n", infilename);

  // OFF requires the index starts from '0'.
  firstnumber = 0;

  while ((bufferp = readline(buffer, fp, &line_count)) != NULL) {
    // Check section
    if (nverts == 0) {
      // Read header 
      bufferp = strstr(bufferp, "OFF");
      if (bufferp != NULL) {
        // Read mesh counts
        bufferp = findnextnumber(bufferp); // Skip field "OFF".
        if (*bufferp == '\0') {
          // Read a non-empty line.
          bufferp = readline(buffer, fp, &line_count);
        }
        if ((sscanf(bufferp, "%d%d%d", &nverts, &nfaces, &nedges) != 3) 
            || (nverts == 0)) {
          //printf("Syntax error reading header on line %d in file %s\n",
                // line_count, infilename);
          fclose(fp);
          return false;
        }
        // Allocate memory for 'tetgenio'
        if (nverts > 0) {
          numberofpoints = nverts;
          pointlist = new REAL[nverts * 3];
        }
        if (nfaces > 0) {        
          numberoffacets = nfaces;
          facetlist = new tetgenio::facet[nfaces];
        }
      }
    } else if (iverts < nverts) {
      // Read vertex coordinates
      coord = &pointlist[iverts * 3];
      for (i = 0; i < 3; i++) {
        if (*bufferp == '\0') {
          //printf("Syntax error reading vertex coords on line %d in file %s\n",
               //  line_count, infilename);
          fclose(fp);
          return false;
        }
        coord[i] = (REAL) strtod(bufferp, &bufferp);
        bufferp = findnextnumber(bufferp);
      }
      iverts++;
    } else if (ifaces < nfaces) {
      // Get next face
      f = &facetlist[ifaces];
      init(f);      
      // In .off format, each facet has one polygon, no hole.
      f->numberofpolygons = 1;
      f->polygonlist = new tetgenio::polygon[1];
      p = &f->polygonlist[0];
      init(p);
      // Read the number of vertices, it should be greater than 0.
      p->numberofvertices = (int) strtol(bufferp, &bufferp, 0);
      if (p->numberofvertices == 0) {
        //printf("Syntax error reading polygon on line %d in file %s\n",
             //  line_count, infilename);
        fclose(fp);
        return false;
      }
      // Allocate memory for face vertices
      p->vertexlist = new int[p->numberofvertices];
      for (i = 0; i < p->numberofvertices; i++) {
        bufferp = findnextnumber(bufferp);
        if (*bufferp == '\0') {
          //printf("Syntax error reading polygon on line %d in file %s\n",
             //    line_count, infilename);
          fclose(fp);
          return false;
        }
        p->vertexlist[i] = (int) strtol(bufferp, &bufferp, 0);
      }
      ifaces++;
    } else {
      // Should never get here
      //printf("Found extra text starting at line %d in file %s\n", line_count,
           //  infilename);
      break;
    }
  }

  // Close file
  fclose(fp);

  // Check whether read all points
  if (iverts != nverts) {
    //printf("Expected %d vertices, but read only %d vertices in file %s\n",
          // nverts, iverts, infilename);
    return false;
  }

  // Check whether read all faces
  if (ifaces != nfaces) {
    //printf("Expected %d faces, but read only %d faces in file %s\n",
           //nfaces, ifaces, infilename);
    return false;
  }

  return true;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// load_ply()    Load a polyhedron from a .ply file.                         //
//                                                                           //
// This is a simplified version of reading .ply files, which only reads the  //
// set of vertices and the set of faces. Other informations (such as color,  //
// material, texture, etc) in .ply file are ignored. Complete routines for   //
// reading and writing ,ply files are available from: http://www.cc.gatech.  //
// edu/projects/large_models/ply.html.  Except the header section, ply file  //
// format has exactly the same format for listing vertices and polygons as   //
// off file format.                                                          //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenio::load_ply(char* filebasename)
{
  FILE *fp;
  tetgenio::facet *f;
  tetgenio::polygon *p;
  char infilename[FILENAMESIZE];
  char buffer[INPUTLINESIZE];
  char *bufferp, *str;
  double *coord;
  int endheader = 0, format = 0;
  int nverts = 0, iverts = 0;
  int nfaces = 0, ifaces = 0;
  int line_count = 0, i;

  strncpy(infilename, filebasename, FILENAMESIZE - 1);
  infilename[FILENAMESIZE - 1] = '\0';
  if (infilename[0] == '\0') {
    //printf("Error:  No filename.\n");
    return false;
  }
  if (strcmp(&infilename[strlen(infilename) - 4], ".ply") != 0) {
    strcat(infilename, ".ply");
  }

  if (!(fp = fopen(infilename, "r"))) {
    //printf("Error:  Unable to open file %s\n", infilename);
    return false;
  }
  //printf("Opening %s.\n", infilename);

  // PLY requires the index starts from '0'.
  firstnumber = 0;

  while ((bufferp = readline(buffer, fp, &line_count)) != NULL) {
    if (!endheader) {
      // Find if it is the keyword "end_header".
      str = strstr(bufferp, "end_header");
      // strstr() is case sensitive.
      if (!str) str = strstr(bufferp, "End_header");
      if (!str) str = strstr(bufferp, "End_Header");
      if (str) {
        // This is the end of the header section.
        endheader = 1; 
        continue;
      }
      // Parse the number of vertices and the number of faces.
      if (nverts == 0 || nfaces == 0) {
        // Find if it si the keyword "element".
        str = strstr(bufferp, "element");
        if (!str) str = strstr(bufferp, "Element");
        if (str) {
          bufferp = findnextfield(str);
          if (*bufferp == '\0') {
            //printf("Syntax error reading element type on line%d in file %s\n",
                   //line_count, infilename);
            fclose(fp);
            return false;
          }
          if (nverts == 0) {
            // Find if it is the keyword "vertex".
            str = strstr(bufferp, "vertex");
            if (!str) str = strstr(bufferp, "Vertex");
            if (str) {
              bufferp = findnextnumber(str);
              if (*bufferp == '\0') {
                //printf("Syntax error reading vertex number on line");
                //printf(" %d in file %s\n", line_count, infilename);
                fclose(fp);
                return false;
              }
              nverts = (int) strtol(bufferp, &bufferp, 0);
              // Allocate memory for 'tetgenio'
              if (nverts > 0) {
                numberofpoints = nverts;
                pointlist = new REAL[nverts * 3];
              }
            }
          }
          if (nfaces == 0) {
            // Find if it is the keyword "face".
            str = strstr(bufferp, "face");
            if (!str) str = strstr(bufferp, "Face");
            if (str) {
              bufferp = findnextnumber(str);
              if (*bufferp == '\0') {
                //printf("Syntax error reading face number on line");
                //printf(" %d in file %s\n", line_count, infilename);
                fclose(fp);
                return false;
              }
              nfaces = (int) strtol(bufferp, &bufferp, 0);
              // Allocate memory for 'tetgenio'
              if (nfaces > 0) {        
                numberoffacets = nfaces;
                facetlist = new tetgenio::facet[nfaces];
              }
            }
          }
        } // It is not the string "element". 
      }
      if (format == 0) {
        // Find the keyword "format".
        str = strstr(bufferp, "format");
        if (!str) str = strstr(bufferp, "Format");
        if (str) {
          format = 1;
          bufferp = findnextfield(str);
          // Find if it is the string "ascii".
          str = strstr(bufferp, "ascii");
          if (!str) str = strstr(bufferp, "ASCII");
          if (!str) {
            //printf("This routine only reads ascii format of ply files.\n");
            //printf("Hint: You can convert the binary to ascii format by\n");
            //printf("  using the provided ply tools:\n");
            //printf("  ply2ascii < %s > ascii_%s\n", infilename, infilename);
            fclose(fp);
            return false;
          }
        }
      }
    } else if (iverts < nverts) {
      // Read vertex coordinates
      coord = &pointlist[iverts * 3];
      for (i = 0; i < 3; i++) {
        if (*bufferp == '\0') {
          //printf("Syntax error reading vertex coords on line %d in file %s\n",
                // line_count, infilename);
          fclose(fp);
          return false;
        }
        coord[i] = (REAL) strtod(bufferp, &bufferp);
        bufferp = findnextnumber(bufferp);
      }
      iverts++;
    } else if (ifaces < nfaces) {
      // Get next face
      f = &facetlist[ifaces];
      init(f);      
      // In .off format, each facet has one polygon, no hole.
      f->numberofpolygons = 1;
      f->polygonlist = new tetgenio::polygon[1];
      p = &f->polygonlist[0];
      init(p);
      // Read the number of vertices, it should be greater than 0.
      p->numberofvertices = (int) strtol(bufferp, &bufferp, 0);
      if (p->numberofvertices == 0) {
        //printf("Syntax error reading polygon on line %d in file %s\n",
               //line_count, infilename);
        fclose(fp);
        return false;
      }
      // Allocate memory for face vertices
      p->vertexlist = new int[p->numberofvertices];
      for (i = 0; i < p->numberofvertices; i++) {
        bufferp = findnextnumber(bufferp);
        if (*bufferp == '\0') {
          //printf("Syntax error reading polygon on line %d in file %s\n",
                // line_count, infilename);
          fclose(fp);
          return false;
        }
        p->vertexlist[i] = (int) strtol(bufferp, &bufferp, 0);
      }
      ifaces++;
    } else {
      // Should never get here
      //printf("Found extra text starting at line %d in file %s\n", line_count,
         //    infilename);
      break;
    }
  }

  // Close file
  fclose(fp);

  // Check whether read all points
  if (iverts != nverts) {
    //printf("Expected %d vertices, but read only %d vertices in file %s\n",
         //  nverts, iverts, infilename);
    return false;
  }

  // Check whether read all faces
  if (ifaces != nfaces) {
    //printf("Expected %d faces, but read only %d faces in file %s\n",
         //  nfaces, ifaces, infilename);
    return false;
  }

  return true;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// load_stl()    Load a surface mesh from a .stl file.                       //
//                                                                           //
// The .stl or stereolithography format is an ASCII or binary file used in   //
// manufacturing.  It is a list of the triangular surfaces that describe a   //
// computer generated solid model. This is the standard input for most rapid //
// prototyping machines.                                                     //
//                                                                           //
// Comment: A .stl file many contain many duplicated points.  They will be   //
// unified during the Delaunay tetrahedralization process.                   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenio::load_stl(char* filebasename)
{
  FILE *fp;
  tetgenmesh::list *plist;
  tetgenio::facet *f;
  tetgenio::polygon *p;
  char infilename[FILENAMESIZE];
  char buffer[INPUTLINESIZE];
  char *bufferp, *str;
  double *coord;
  int solid = 0;
  int nverts = 0, iverts = 0;
  int nfaces = 0;
  int line_count = 0, i;

  strncpy(infilename, filebasename, FILENAMESIZE - 1);
  infilename[FILENAMESIZE - 1] = '\0';
  if (infilename[0] == '\0') {
    //printf("Error:  No filename.\n");
    return false;
  }
  if (strcmp(&infilename[strlen(infilename) - 4], ".stl") != 0) {
    strcat(infilename, ".stl");
  }

  if (!(fp = fopen(infilename, "r"))) {
    //printf("Error:  Unable to open file %s\n", infilename);
    return false;
  }
  //printf("Opening %s.\n", infilename);

  // STL file has no number of points available. Use a list to read points.
  plist = new tetgenmesh::list(sizeof(double) * 3, NULL, 1024); 

  while ((bufferp = readline(buffer, fp, &line_count)) != NULL) {
    // The ASCII .stl file must start with the lower case keyword solid and
    //   end with endsolid.
    if (solid == 0) {
      // Read header 
      bufferp = strstr(bufferp, "solid");
      if (bufferp != NULL) {
        solid = 1;
      }
    } else {
      // We're inside the block of the solid.
      str = bufferp;
      // Is this the end of the solid.
      bufferp = strstr(bufferp, "endsolid");
      if (bufferp != NULL) {
        solid = 0;
      } else {
        // Read the XYZ coordinates if it is a vertex.
        bufferp = str;
        bufferp = strstr(bufferp, "vertex");
        if (bufferp != NULL) {
          coord = (double *) plist->append(NULL);
          for (i = 0; i < 3; i++) {
            bufferp = findnextnumber(bufferp);
            if (*bufferp == '\0') {
              //printf("Syntax error reading vertex coords on line %d\n",
                  // line_count);
              delete plist;
              fclose(fp);
              return false;
            }
            coord[i] = (REAL) strtod(bufferp, &bufferp);
          }
        }
      }
    }
  }
  fclose(fp);

  nverts = plist->len();
  // nverts should be an integer times 3 (every 3 vertices denote a face).
  if (nverts == 0 || (nverts % 3 != 0)) {
    //printf("Error:  Wrong number of vertices in file %s.\n", infilename);
    delete plist;
    return false;
  }
  numberofpoints = nverts;
  pointlist = new REAL[nverts * 3];
  for (i = 0; i < nverts; i++) {
    coord = (double *) (* plist)[i];
    iverts = i * 3;
    pointlist[iverts] = (REAL) coord[0];
    pointlist[iverts + 1] = (REAL) coord[1];
    pointlist[iverts + 2] = (REAL) coord[2];
  }

  nfaces = (int) (nverts / 3);
  numberoffacets = nfaces;
  facetlist = new tetgenio::facet[nfaces];

  // Default use '1' as the array starting index.
  firstnumber = 1;
  iverts = firstnumber;
  for (i = 0; i < nfaces; i++) {
    f = &facetlist[i];
    init(f);      
    // In .stl format, each facet has one polygon, no hole.
    f->numberofpolygons = 1;
    f->polygonlist = new tetgenio::polygon[1];
    p = &f->polygonlist[0];
    init(p);
    // Each polygon has three vertices.
    p->numberofvertices = 3;
    p->vertexlist = new int[p->numberofvertices];
    p->vertexlist[0] = iverts;
    p->vertexlist[1] = iverts + 1;
    p->vertexlist[2] = iverts + 2;
    iverts += 3;
  }

  delete plist;
  return true;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// load_medit()    Load a surface mesh from a .mesh file.                    //
//                                                                           //
// The .mesh format is the file format of Medit, a user-friendly interactive //
// mesh viewer program.                                                      //
//                                                                           //
// This routine ONLY reads the sections containing vertices, triangles, and  //
// quadrilaters. Other sections (such as tetrahedra, edges, ...) are ignored.//
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenio::load_medit(char* filebasename)
{
  FILE *fp;
  tetgenio::facet *tmpflist, *f;
  tetgenio::polygon *p;
  char infilename[FILENAMESIZE];
  char buffer[INPUTLINESIZE];
  char *bufferp, *str;
  double *coord;
  int *tmpfmlist;
  int dimension = 0;
  int nverts = 0;
  int nfaces = 0;
  int line_count = 0;
  int corners = 0; // 3 (triangle) or 4 (quad).
  int i, j;

  strncpy(infilename, filebasename, FILENAMESIZE - 1);
  infilename[FILENAMESIZE - 1] = '\0';
  if (infilename[0] == '\0') {
    //printf("Error:  No filename.\n");
    return false;
  }
  if (strcmp(&infilename[strlen(infilename) - 5], ".mesh") != 0) {
    strcat(infilename, ".mesh");
  }
  
  if (!(fp = fopen(infilename, "r"))) {
    //printf("Error:  Unable to open file %s\n", infilename);
    return false;
  }
  //printf("Opening %s.\n", infilename);

  // Default uses the index starts from '1'.
  firstnumber = 1;

  while ((bufferp = readline(buffer, fp, &line_count)) != NULL) {
    if (*bufferp == '#') continue;  // A comment line is skipped.
    if (dimension == 0) {
      // Find if it is the keyword "Dimension".
      str = strstr(bufferp, "Dimension");
      if (!str) str = strstr(bufferp, "dimension");
      if (!str) str = strstr(bufferp, "DIMENSION");
      if (str) {
        // Read the dimensions
        bufferp = findnextnumber(str); // Skip field "Dimension".
        if (*bufferp == '\0') {
          // Read a non-empty line.
          bufferp = readline(buffer, fp, &line_count);
        }
        dimension = (int) strtol(bufferp, &bufferp, 0);
        if (dimension != 2 && dimension != 3) {
          //printf("Unknown dimension in file on line %d in file %s\n",
                 //line_count, infilename);
          fclose(fp);
          return false;
        }
        mesh_dim = dimension;
      }
    }
    if (nverts == 0) {
      // Find if it is the keyword "Vertices".
      str = strstr(bufferp, "Vertices");
      if (!str) str = strstr(bufferp, "vertices");
      if (!str) str = strstr(bufferp, "VERTICES");
      if (str) {
        // Read the number of vertices.
        bufferp = findnextnumber(str); // Skip field "Vertices".
        if (*bufferp == '\0') {
          // Read a non-empty line.
          bufferp = readline(buffer, fp, &line_count);
        }
        nverts = (int) strtol(bufferp, &bufferp, 0);
        // Allocate memory for 'tetgenio'
        if (nverts > 0) {
          numberofpoints = nverts;
          pointlist = new REAL[nverts * 3];
        }
        // Read the follwoing node list.
        for (i = 0; i < nverts; i++) {
          bufferp = readline(buffer, fp, &line_count);
          if (bufferp == NULL) {
            //printf("Unexpected end of file on line %d in file %s\n",
                  // line_count, infilename);
            fclose(fp);
            return false;
          }
          // Read vertex coordinates
          coord = &pointlist[i * 3];
          for (j = 0; j < 3; j++) {
            if (*bufferp == '\0') {
              //printf("Syntax error reading vertex coords on line");
              //printf(" %d in file %s\n", line_count, infilename);
              fclose(fp);
              return false;
            }
            if ((j < 2) || (dimension == 3)) {
              coord[j] = (REAL) strtod(bufferp, &bufferp);
            } else {
              //assert((j == 2) && (dimension == 2));
              coord[j] = 0.0;
            }
            bufferp = findnextnumber(bufferp);
          }
        }
        continue;
      }
    } 
    if (nfaces == 0) {
      // Find if it is the keyword "Triangles" or "Quadrilaterals".
      corners = 0;
      str = strstr(bufferp, "Triangles");
      if (!str) str = strstr(bufferp, "triangles");
      if (!str) str = strstr(bufferp, "TRIANGLES");
      if (str) {
        corners = 3;
      } else {
        str = strstr(bufferp, "Quadrilaterals");
        if (!str) str = strstr(bufferp, "quadrilaterals");
        if (!str) str = strstr(bufferp, "QUADRILATERALS");
        if (str) {
          corners = 4;
        }
      }
      if (corners == 3 || corners == 4) {
        // Read the number of triangles (or quadrilaterals).
        bufferp = findnextnumber(str); // Skip field "Triangles".
        if (*bufferp == '\0') {
          // Read a non-empty line.
          bufferp = readline(buffer, fp, &line_count);
        }
        nfaces = strtol(bufferp, &bufferp, 0);
        // Allocate memory for 'tetgenio'
        if (nfaces > 0) {
          if (numberoffacets > 0) {
            // facetlist has already been allocated. Enlarge arrays.
            tmpflist = new tetgenio::facet[numberoffacets + nfaces];
            tmpfmlist = new int[numberoffacets + nfaces];
            // Copy the data of old arrays into new arrays.
            for (i = 0; i < numberoffacets; i++) {
              f = &(tmpflist[i]);
              tetgenio::init(f);
              *f = facetlist[i];
              tmpfmlist[i] = facetmarkerlist[i];
            }
            // Release old arrays.
            delete [] facetlist;
            delete [] facetmarkerlist;
            // Remember the new arrays.
            facetlist = tmpflist;
            facetmarkerlist = tmpfmlist;
          } else {
            // This is the first time to allocate facetlist.
            facetlist = new tetgenio::facet[nfaces];
            facetmarkerlist = new int[nfaces];
          }
        }
        // Read the following list of faces.
        for (i = numberoffacets; i < numberoffacets + nfaces; i++) {
          bufferp = readline(buffer, fp, &line_count);
          if (bufferp == NULL) {
            //printf("Unexpected end of file on line %d in file %s\n",
                   //line_count, infilename);
            fclose(fp);
            return false;
          }
          f = &facetlist[i];
          tetgenio::init(f);
          // In .mesh format, each facet has one polygon, no hole.
          f->numberofpolygons = 1;
          f->polygonlist = new tetgenio::polygon[1];
          p = &f->polygonlist[0];
          tetgenio::init(p);
          p->numberofvertices = corners;
          // Allocate memory for face vertices
          p->vertexlist = new int[p->numberofvertices];
          // Read the vertices of the face.
          for (j = 0; j < corners; j++) {
            if (*bufferp == '\0') {
              //printf("Syntax error reading face on line %d in file %s\n",
                     //line_count, infilename);
              fclose(fp);
              return false;
            }
            p->vertexlist[j] = (int) strtol(bufferp, &bufferp, 0);
            if (firstnumber == 1) {
              // Check if a '0' index appears.
              if (p->vertexlist[j] == 0) {
                // The first index is set to be 0.
                firstnumber = 0;
              }
            }
            bufferp = findnextnumber(bufferp);
          }
          // Read the marker of the face if it exists.
          facetmarkerlist[i] = 0;
          if (*bufferp != '\0') {
            facetmarkerlist[i] = (int) strtol(bufferp, &bufferp, 0);
          }
        }
        // Have read in a list of triangles/quads.
        numberoffacets += nfaces;
        nfaces = 0;
      }
    }
    // if (nverts > 0 && nfaces > 0) break; // Ignore other data.
  }

  // Close file
  fclose(fp);

  return true;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// load_vtk()    Load VTK surface mesh from file (.vtk ascii or binary).     //
//                                                                           //
// This function is contributed by: Bryn Lloyd, Computer Vision Laborator,   //
// ETH, Zuerich. May 7, 2007.                                                //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenio::load_vtk(char* filebasename)
{
  FILE *fp;
  tetgenio::facet *f;
  tetgenio::polygon *p;
  char infilename[FILENAMESIZE];
  char line[INPUTLINESIZE];
  char mode[128], id[256], fmt[64];
  char *bufferp;
  double *coord;
  float _x, _y, _z;
  int nverts = 0;
  int nfaces = 0;
  int line_count = 0;
  int dummy;
  int id1, id2, id3;
  int nn = -1;
  int nn_old = -1;
  int i, j;
  bool ImALittleEndian = !testIsBigEndian();

  strncpy(infilename, filebasename, FILENAMESIZE - 1);
  infilename[FILENAMESIZE - 1] = '\0';
  if (infilename[0] == '\0') {
    //printf("Error:  No filename.\n");
    return false;
  }
  if (strcmp(&infilename[strlen(infilename) - 4], ".vtk") != 0) {
    strcat(infilename, ".vtk");
  }
  if (!(fp = fopen(infilename, "r"))) {
    //printf("Error:  Unable to open file %s\n", infilename);
    return false;
  }
  //printf("Opening %s.\n", infilename);

  // Default uses the index starts from '0'.
  firstnumber = 0;
  strcpy(mode, "BINARY");

  while((bufferp = readline(line, fp, &line_count)) != NULL) {
    if(strlen(line) == 0) continue;
    //swallow lines beginning with a comment sign or white space
    if(line[0] == '#' || line[0]=='\n' || line[0] == 10 || line[0] == 13 || 
       line[0] == 32) continue;

    sscanf(line, "%s", id);
    if(!strcmp(id, "ASCII")) {
      strcpy(mode, "ASCII");
    }

    if(!strcmp(id, "POINTS")) {
      sscanf(line, "%s %d %s", id, &nverts, fmt);
      if (nverts > 0) {
        numberofpoints = nverts;
        pointlist = new REAL[nverts * 3];
      }

      if(!strcmp(mode, "BINARY")) {
        for(i = 0; i < nverts; i++) {
          coord = &pointlist[i * 3];
          if(!strcmp(fmt, "double")) {
            fread((char*)(&(coord[0])), sizeof(double), 1, fp);
            fread((char*)(&(coord[1])), sizeof(double), 1, fp);
            fread((char*)(&(coord[2])), sizeof(double), 1, fp);
            if(ImALittleEndian){
              swapBytes((unsigned char *) &(coord[0]), sizeof(coord[0]));
              swapBytes((unsigned char *) &(coord[1]), sizeof(coord[1]));
              swapBytes((unsigned char *) &(coord[2]), sizeof(coord[2]));
            }
          } else if(!strcmp(fmt, "float")) {
            fread((char*)(&_x), sizeof(float), 1, fp);
            fread((char*)(&_y), sizeof(float), 1, fp);
            fread((char*)(&_z), sizeof(float), 1, fp);
            if(ImALittleEndian){
              swapBytes((unsigned char *) &_x, sizeof(_x));
              swapBytes((unsigned char *) &_y, sizeof(_y));
              swapBytes((unsigned char *) &_z, sizeof(_z));
            }
            coord[0] = double(_x);
            coord[1] = double(_y);
            coord[2] = double(_z);
          } else {
            //printf("Error: Only float or double formats are supported!\n");
            return false;
          }
        }
      } else if(!strcmp(mode, "ASCII")) {
        for(i = 0; i < nverts; i++){
          bufferp = readline(line, fp, &line_count);
          if (bufferp == NULL) {
            //printf("Unexpected end of file on line %d in file %s\n",
                   //line_count, infilename);
            fclose(fp);
            return false;
          }
          // Read vertex coordinates
          coord = &pointlist[i * 3];
          for (j = 0; j < 3; j++) {
            if (*bufferp == '\0') {
              //printf("Syntax error reading vertex coords on line");
              //printf(" %d in file %s\n", line_count, infilename);
              fclose(fp);
              return false;
            }
            coord[j] = (REAL) strtod(bufferp, &bufferp);
            bufferp = findnextnumber(bufferp);
          }
        }
      }
      continue;
    }

    if(!strcmp(id, "POLYGONS")) {
      sscanf(line, "%s %d  %d", id, &nfaces, &dummy);
      if (nfaces > 0) {
        numberoffacets = nfaces;
        facetlist = new tetgenio::facet[nfaces];
      }

      if(!strcmp(mode, "BINARY")) {
        for(i = 0; i < nfaces; i++){
          fread((char*)(&nn), sizeof(int), 1, fp);
          if(ImALittleEndian){
            swapBytes((unsigned char *) &nn, sizeof(nn));
          }
          if (i == 0)
            nn_old = nn;
          if (nn != nn_old) {
            //printf("Error:  No mixed cells are allowed.\n");
            return false;
          }

          if(nn == 3){
            fread((char*)(&id1), sizeof(int), 1, fp);
            fread((char*)(&id2), sizeof(int), 1, fp);
            fread((char*)(&id3), sizeof(int), 1, fp);
            if(ImALittleEndian){
              swapBytes((unsigned char *) &id1, sizeof(id1));
              swapBytes((unsigned char *) &id2, sizeof(id2));
              swapBytes((unsigned char *) &id3, sizeof(id3));
            }
            f = &facetlist[i];
            init(f);
            // In .off format, each facet has one polygon, no hole.
            f->numberofpolygons = 1;
            f->polygonlist = new tetgenio::polygon[1];
            p = &f->polygonlist[0];
            init(p);
            // Set number of vertices
            p->numberofvertices = 3;
            // Allocate memory for face vertices
            p->vertexlist = new int[p->numberofvertices];
            p->vertexlist[0] = id1;
            p->vertexlist[1] = id2;
            p->vertexlist[2] = id3;
          } else {
            //printf("Error: Only triangles are supported\n");
            return false;
          }
        }
      } else if(!strcmp(mode, "ASCII")) {
        for(i = 0; i < nfaces; i++) {
          bufferp = readline(line, fp, &line_count);
          nn = (int) strtol(bufferp, &bufferp, 0);
          if (i == 0)
            nn_old = nn;
          if (nn != nn_old) {
            //printf("Error:  No mixed cells are allowed.\n");
            return false;
          }

          if (nn == 3) {
            bufferp = findnextnumber(bufferp); // Skip the first field.
            id1 = (int) strtol(bufferp, &bufferp, 0);
            bufferp = findnextnumber(bufferp);
            id2 = (int) strtol(bufferp, &bufferp, 0);
            bufferp = findnextnumber(bufferp);
            id3 = (int) strtol(bufferp, &bufferp, 0);
            f = &facetlist[i];
            init(f);
            // In .off format, each facet has one polygon, no hole.
            f->numberofpolygons = 1;
            f->polygonlist = new tetgenio::polygon[1];
            p = &f->polygonlist[0];
            init(p);
            // Set number of vertices
            p->numberofvertices = 3;
            // Allocate memory for face vertices
            p->vertexlist = new int[p->numberofvertices];
            p->vertexlist[0] = id1;
            p->vertexlist[1] = id2;
            p->vertexlist[2] = id3;
          } else {
            //printf("Error:  Only triangles are supported.\n");
            return false;
          }
        }
      }

      fclose(fp);
      return true;
    }

    if(!strcmp(id,"LINES") || !strcmp(id,"CELLS")){
      //printf("Warning:  load_vtk(): cannot read formats LINES, CELLS.\n");
    }
  } // while ()

  return true;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// load_plc()    Load a piecewise linear complex from file(s).               //
//                                                                           //
// 'object'  indicates which file format is used to describ the plc.         //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenio::load_plc(char* filebasename, int object)
{
  enum tetgenbehavior::objecttype type;

  type = (enum tetgenbehavior::objecttype) object;
  switch (type) {
  case tetgenbehavior::NODES:
    return load_node(filebasename);
  case tetgenbehavior::POLY:
    return load_poly(filebasename);
  case tetgenbehavior::OFF:
    return load_off(filebasename);
  case tetgenbehavior::PLY:
    return load_ply(filebasename);
  case tetgenbehavior::STL:
    return load_stl(filebasename);
  case tetgenbehavior::MEDIT:
    return load_medit(filebasename);
  case tetgenbehavior::VTK:
    return load_vtk(filebasename);
  default:
    return load_poly(filebasename);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// load_tetmesh()    Load a tetrahedral mesh from files.                     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenio::load_tetmesh(char* filebasename)
{
  FILE *infile;
  char innodefilename[FILENAMESIZE];
  char inelefilename[FILENAMESIZE];
  char infacefilename[FILENAMESIZE];
  char inedgefilename[FILENAMESIZE];
  char involfilename[FILENAMESIZE];
  char inputline[INPUTLINESIZE];
  char *stringptr, *infilename;
  REAL attrib, volume;
  int volelements;
  int markers, corner;
  int index, attribindex;
  int i, j;

  // Assembling the actual file names we want to open.
  strcpy(innodefilename, filebasename);
  strcpy(inelefilename, filebasename);
  strcpy(infacefilename, filebasename);
  strcpy(inedgefilename, filebasename);
  strcpy(involfilename, filebasename);
  strcat(innodefilename, ".node");
  strcat(inelefilename, ".ele");
  strcat(infacefilename, ".face");
  strcat(inedgefilename, ".edge");
  strcat(involfilename, ".vol");

  // Read the points from a .node file.
  infilename = innodefilename;
  //printf("Opening %s.\n", infilename);
  infile = fopen(infilename, "r");
  if (infile == (FILE *) NULL) {
    //printf("File I/O Error:  Cannot access file %s.\n", infilename);
    return false;
  }
  // Read the first line of the file.
  stringptr = readnumberline(inputline, infile, infilename); 
  // Is this list of points generated from rbox?
  stringptr = strstr(inputline, "rbox");
  if (stringptr == NULL) {
    // Read number of points, number of dimensions, number of point
    //   attributes, and number of boundary markers.
    stringptr = inputline;
    numberofpoints = (int) strtol (stringptr, &stringptr, 0);
    stringptr = findnextnumber(stringptr);
    if (*stringptr == '\0') {
      mesh_dim = 3;
    } else {
      mesh_dim = (int) strtol (stringptr, &stringptr, 0);
    }
    stringptr = findnextnumber(stringptr);
    if (*stringptr == '\0') {
      numberofpointattributes = 0;
    } else {
      numberofpointattributes = (int) strtol (stringptr, &stringptr, 0);
    }
    stringptr = findnextnumber(stringptr);
    if (*stringptr == '\0') {
      markers = 0;  // Default value.
    } else {
      markers = (int) strtol (stringptr, &stringptr, 0);
    }
  } else {
    // It is a rbox (qhull) input file.
    stringptr = inputline;
    // Get the dimension.
    mesh_dim = (int) strtol (stringptr, &stringptr, 0);
    // Get the number of points.
    stringptr = readnumberline(inputline, infile, infilename);
    numberofpoints = (int) strtol (stringptr, &stringptr, 0);
    // There is no index column.
    useindex = 0;
  }

  // Load the list of nodes.
  if (!load_node_call(infile, markers, infilename)) {
    fclose(infile);
    return false;
  }
  fclose(infile);

  // Read the elements from an .ele file.
  if (mesh_dim == 3) {
    infilename = inelefilename;
    infile = fopen(infilename, "r");
    if (infile != (FILE *) NULL) {
      //printf("Opening %s.\n", infilename);
      // Read number of elements, number of corners (4 or 10), number of
      //   element attributes.
      stringptr = readnumberline(inputline, infile, infilename);
      numberoftetrahedra = (int) strtol (stringptr, &stringptr, 0);
      stringptr = findnextnumber(stringptr);
      if (*stringptr == '\0') {
        numberofcorners = 4;  // Default read 4 nodes per element.
      } else {
        numberofcorners = (int) strtol(stringptr, &stringptr, 0);
      }
      stringptr = findnextnumber(stringptr);
      if (*stringptr == '\0') {
        numberoftetrahedronattributes = 0; // Default no attribute.
      } else {
        numberoftetrahedronattributes = (int) strtol(stringptr, &stringptr, 0);
      }
      if (numberofcorners != 4 && numberofcorners != 10) {
        //printf("Error:  Wrong number of corners %d (should be 4 or 10).\n", 
               //numberofcorners);
        fclose(infile);
        return false;
      }
      // Allocate memory for tetrahedra.
      if (numberoftetrahedra > 0) {
        tetrahedronlist = new int[numberoftetrahedra * numberofcorners]; 
        if (tetrahedronlist == (int *) NULL) {
          terminatetetgen(1);
        }
        // Allocate memory for output tetrahedron attributes if necessary.
        if (numberoftetrahedronattributes > 0) {
          tetrahedronattributelist = new REAL[numberoftetrahedra *
                                          numberoftetrahedronattributes];
          if (tetrahedronattributelist == (REAL *) NULL) {
            terminatetetgen(1);
          }
        }
      }
      // Read the list of tetrahedra.
      index = 0;
      attribindex = 0;
      for (i = 0; i < numberoftetrahedra; i++) {
        // Read tetrahedron index and the tetrahedron's corners.
        stringptr = readnumberline(inputline, infile, infilename);
        for (j = 0; j < numberofcorners; j++) {
          stringptr = findnextnumber(stringptr);
          if (*stringptr == '\0') {
            //printf("Error:  Tetrahedron %d is missing vertex %d in %s.\n",
                  // i + firstnumber, j + 1, infilename);
            terminatetetgen(1);
          }
          corner = (int) strtol(stringptr, &stringptr, 0);
          if (corner < firstnumber || corner >= numberofpoints + firstnumber) {
            //printf("Error:  Tetrahedron %d has an invalid vertex index.\n",
                   //i + firstnumber);
            terminatetetgen(1);
          }
          tetrahedronlist[index++] = corner;
        }
        // Read the tetrahedron's attributes.
        for (j = 0; j < numberoftetrahedronattributes; j++) {
          stringptr = findnextnumber(stringptr);
          if (*stringptr == '\0') {
            attrib = 0.0;
          } else {
            attrib = (REAL) strtod(stringptr, &stringptr);
          }
          tetrahedronattributelist[attribindex++] = attrib;
        }
      }
      fclose(infile);
    }
  } // if (meshdim == 3)
  
  // Read the hullfaces or subfaces from a .face file if it exists.
  if (mesh_dim == 3) {
    infilename = infacefilename;
  } else {
    infilename = inelefilename;
  }
  infile = fopen(infilename, "r");
  if (infile != (FILE *) NULL) {
    //printf("Opening %s.\n", infilename);
    // Read number of faces, boundary markers.
    stringptr = readnumberline(inputline, infile, infilename);
    numberoftrifaces = (int) strtol (stringptr, &stringptr, 0);
    stringptr = findnextnumber(stringptr);
    if (mesh_dim == 2) {
      // Skip a number.
      stringptr = findnextnumber(stringptr);
    }
    if (*stringptr == '\0') {
      markers = 0;  // Default there is no marker per face.
    } else {
      markers = (int) strtol (stringptr, &stringptr, 0);
    }
    if (numberoftrifaces > 0) {
      trifacelist = new int[numberoftrifaces * 3];
      if (trifacelist == (int *) NULL) {
        terminatetetgen(1);
      }
      if (markers) {
        trifacemarkerlist = new int[numberoftrifaces];
        if (trifacemarkerlist == (int *) NULL) {
          terminatetetgen(1);
        }
      }
    }
    // Read the list of faces.
    index = 0;
    for (i = 0; i < numberoftrifaces; i++) {
      // Read face index and the face's three corners.
      stringptr = readnumberline(inputline, infile, infilename);
      for (j = 0; j < 3; j++) {
        stringptr = findnextnumber(stringptr);
        if (*stringptr == '\0') {
          //printf("Error:  Face %d is missing vertex %d in %s.\n",
                // i + firstnumber, j + 1, infilename);
          terminatetetgen(1);
        }
        corner = (int) strtol(stringptr, &stringptr, 0);
        if (corner < firstnumber || corner >= numberofpoints + firstnumber) {
          //printf("Error:  Face %d has an invalid vertex index.\n",
                // i + firstnumber);
          terminatetetgen(1);
        }
        trifacelist[index++] = corner;
      }
      // Read the boundary marker if it exists.
      if (markers) {
        stringptr = findnextnumber(stringptr);
        if (*stringptr == '\0') {
          attrib = 0.0;
        } else {
          attrib = (REAL) strtod(stringptr, &stringptr);
        }
        trifacemarkerlist[i] = (int) attrib;
      }
    }
    fclose(infile);
  }

  // Read the boundary edges from a .edge file if it exists.
  infilename = inedgefilename;
  infile = fopen(infilename, "r");
  if (infile != (FILE *) NULL) {
    //printf("Opening %s.\n", infilename);
    // Read number of boundary edges.
    stringptr = readnumberline(inputline, infile, infilename);
    numberofedges = (int) strtol (stringptr, &stringptr, 0);
    if (numberofedges > 0) {
      edgelist = new int[numberofedges * 2];
      if (edgelist == (int *) NULL) {
        terminatetetgen(1);
      }
      stringptr = findnextnumber(stringptr);
      if (*stringptr == '\0') {
        markers = 0;  // Default value.
      } else {
        markers = (int) strtol (stringptr, &stringptr, 0);
      }
      if (markers > 0) {
        edgemarkerlist = new int[numberofedges];
      }
    }
    // Read the list of faces.
    index = 0;
    for (i = 0; i < numberofedges; i++) {
      // Read face index and the edge's two endpoints.
      stringptr = readnumberline(inputline, infile, infilename);
      for (j = 0; j < 2; j++) {
        stringptr = findnextnumber(stringptr);
        if (*stringptr == '\0') {
          //printf("Error:  Edge %d is missing vertex %d in %s.\n",
                 //i + firstnumber, j + 1, infilename);
          terminatetetgen(1);
        }
        corner = (int) strtol(stringptr, &stringptr, 0);
        if (corner < firstnumber || corner >= numberofpoints + firstnumber) {
          //printf("Error:  Edge %d has an invalid vertex index.\n",
                // i + firstnumber);
          terminatetetgen(1);
        }
        edgelist[index++] = corner;
      }
      // Read the edge marker if it has.
      if (markers) {
        stringptr = findnextnumber(stringptr);
        edgemarkerlist[i] = (int) strtol(stringptr, &stringptr, 0);
      }
    }
    fclose(infile);
  }

  // Read the volume constraints from a .vol file if it exists.
  infilename = involfilename;
  infile = fopen(infilename, "r");
  if (infile != (FILE *) NULL) {
    //printf("Opening %s.\n", infilename);
    // Read number of tetrahedra.
    stringptr = readnumberline(inputline, infile, infilename);
    volelements = (int) strtol (stringptr, &stringptr, 0);
    if (volelements != numberoftetrahedra) {
      //printf("Warning:  %s and %s disagree on number of tetrahedra.\n",
             //inelefilename, involfilename);
      volelements = 0;
    }
    if (volelements > 0) {
      tetrahedronvolumelist = new REAL[volelements];
      if (tetrahedronvolumelist == (REAL *) NULL) {
        terminatetetgen(1);
      }
    }
    // Read the list of volume constraints.
    for (i = 0; i < volelements; i++) {
      stringptr = readnumberline(inputline, infile, infilename);
      stringptr = findnextnumber(stringptr);
      if (*stringptr == '\0') {
        volume = -1.0; // No constraint on this tetrahedron.
      } else {
        volume = (REAL) strtod(stringptr, &stringptr);
      }
      tetrahedronvolumelist[i] = volume;
    }
    fclose(infile);
  }

  // Try to load a .mtr file if it exists.
  load_mtr(filebasename);

  // Try to read a .pbc file if it exists.
  // load_pbc(filebasename);

  return true;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// save_nodes()    Save points to a .node file.                              //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenio::save_nodes(char* filebasename)
{
  FILE *fout;
  char outnodefilename[FILENAMESIZE];
  char outmtrfilename[FILENAMESIZE];
  int i, j;

  sprintf(outnodefilename, "%s.node", filebasename);
  //printf("Saving nodes to %s\n", outnodefilename);
  fout = fopen(outnodefilename, "w");
  fprintf(fout, "%d  %d  %d  %d\n", numberofpoints, mesh_dim,
          numberofpointattributes, pointmarkerlist != NULL ? 1 : 0);
  for (i = 0; i < numberofpoints; i++) {
    if (mesh_dim == 2) {
      fprintf(fout, "%d  %.16g  %.16g", i + firstnumber, pointlist[i * 3],
              pointlist[i * 3 + 1]);
    } else {
      fprintf(fout, "%d  %.16g  %.16g  %.16g", i + firstnumber,
              pointlist[i * 3], pointlist[i * 3 + 1], pointlist[i * 3 + 2]);
    }
    for (j = 0; j < numberofpointattributes; j++) {
      fprintf(fout, "  %.16g", 
              pointattributelist[i * numberofpointattributes + j]);
    }
    if (pointmarkerlist != NULL) {
      fprintf(fout, "  %d", pointmarkerlist[i]);
    }
    fprintf(fout, "\n");
  }
  fclose(fout);

  // If the point metrics exist, output them to a .mtr file.
  if ((numberofpointmtrs > 0) && (pointmtrlist != (REAL *) NULL)) {
    sprintf(outmtrfilename, "%s.mtr", filebasename);
    //printf("Saving metrics to %s\n", outmtrfilename);
    fout = fopen(outmtrfilename, "w");
    fprintf(fout, "%d  %d\n", numberofpoints, numberofpointmtrs);
    for (i = 0; i < numberofpoints; i++) {
      for (j = 0; j < numberofpointmtrs; j++) {
        fprintf(fout, "%.16g ", pointmtrlist[i * numberofpointmtrs + j]);
      }
      fprintf(fout, "\n");
    }
    fclose(fout);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// save_elements()    Save elements to a .ele file.                          //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenio::save_elements(char* filebasename)
{
  FILE *fout;
  char outelefilename[FILENAMESIZE];
  int i, j;

  sprintf(outelefilename, "%s.ele", filebasename);
  //printf("Saving elements to %s\n", outelefilename);
  fout = fopen(outelefilename, "w");
  if (mesh_dim == 3) {
    fprintf(fout, "%d  %d  %d\n", numberoftetrahedra, numberofcorners,
            numberoftetrahedronattributes);
    for (i = 0; i < numberoftetrahedra; i++) {
      fprintf(fout, "%d", i + firstnumber);
      for (j = 0; j < numberofcorners; j++) {
        fprintf(fout, "  %5d", tetrahedronlist[i * numberofcorners + j]);
      }
      for (j = 0; j < numberoftetrahedronattributes; j++) {
        fprintf(fout, "  %g",
          tetrahedronattributelist[i * numberoftetrahedronattributes + j]);
      }
      fprintf(fout, "\n");
    }
  } else {
    // Save a two-dimensional mesh.
    fprintf(fout, "%d  %d  %d\n",numberoftrifaces,3,trifacemarkerlist ? 1 : 0);
    for (i = 0; i < numberoftrifaces; i++) {
      fprintf(fout, "%d", i + firstnumber);
      for (j = 0; j < 3; j++) {
        fprintf(fout, "  %5d", trifacelist[i * 3 + j]);
      }
      if (trifacemarkerlist != NULL) {
        fprintf(fout, "  %d", trifacemarkerlist[i]);
      }
      fprintf(fout, "\n");
    }
  }

  fclose(fout);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// save_faces()    Save faces to a .face file.                               //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenio::save_faces(char* filebasename)
{
  FILE *fout;
  char outfacefilename[FILENAMESIZE];
  int i;

  sprintf(outfacefilename, "%s.face", filebasename);
  //printf("Saving faces to %s\n", outfacefilename);
  fout = fopen(outfacefilename, "w");
  fprintf(fout, "%d  %d\n", numberoftrifaces, 
          trifacemarkerlist != NULL ? 1 : 0);
  for (i = 0; i < numberoftrifaces; i++) {
    fprintf(fout, "%d  %5d  %5d  %5d", i + firstnumber, trifacelist[i * 3],
            trifacelist[i * 3 + 1], trifacelist[i * 3 + 2]);
    if (trifacemarkerlist != NULL) {
      fprintf(fout, "  %d", trifacemarkerlist[i]);
    }
    fprintf(fout, "\n");
  }

  fclose(fout);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// save_edges()    Save egdes to a .edge file.                               //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenio::save_edges(char* filebasename)
{
  FILE *fout;
  char outedgefilename[FILENAMESIZE];
  int i;

  sprintf(outedgefilename, "%s.edge", filebasename);
  //printf("Saving edges to %s\n", outedgefilename);
  fout = fopen(outedgefilename, "w");
  fprintf(fout, "%d  %d\n", numberofedges, edgemarkerlist != NULL ? 1 : 0);
  for (i = 0; i < numberofedges; i++) {
    fprintf(fout, "%d  %4d  %4d", i + firstnumber, edgelist[i * 2],
            edgelist[i * 2 + 1]);
    if (edgemarkerlist != NULL) {
      fprintf(fout, "  %d", edgemarkerlist[i]);
    }
    fprintf(fout, "\n");
  }

  fclose(fout);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// save_neighbors()    Save egdes to a .neigh file.                          //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenio::save_neighbors(char* filebasename)
{
  FILE *fout;
  char outneighborfilename[FILENAMESIZE];
  int i;

  sprintf(outneighborfilename, "%s.neigh", filebasename);
  //printf("Saving neighbors to %s\n", outneighborfilename);
  fout = fopen(outneighborfilename, "w");
  fprintf(fout, "%d  %d\n", numberoftetrahedra, mesh_dim + 1);
  for (i = 0; i < numberoftetrahedra; i++) {
    if (mesh_dim == 2) {
      fprintf(fout, "%d  %5d  %5d  %5d", i + firstnumber,  neighborlist[i * 3],
              neighborlist[i * 3 + 1], neighborlist[i * 3 + 2]);
    } else {
      fprintf(fout, "%d  %5d  %5d  %5d  %5d", i + firstnumber,
              neighborlist[i * 4], neighborlist[i * 4 + 1],
              neighborlist[i * 4 + 2], neighborlist[i * 4 + 3]);
    }
    fprintf(fout, "\n");
  }

  fclose(fout);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// save_poly()    Save segments or facets to a .poly file.                   //
//                                                                           //
// It only save the facets, holes and regions. No .node file is saved.       //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenio::save_poly(char* filebasename)
{
  FILE *fout;
  facet *f;
  polygon *p;
  char outpolyfilename[FILENAMESIZE];
  int i, j, k;

  sprintf(outpolyfilename, "%s.poly", filebasename);
  //printf("Saving poly to %s\n", outpolyfilename);
  fout = fopen(outpolyfilename, "w");

  // The zero indicates that the vertices are in a separate .node file.
  //   Followed by number of dimensions, number of vertex attributes,
  //   and number of boundary markers (zero or one).
  fprintf(fout, "%d  %d  %d  %d\n", 0, mesh_dim, numberofpointattributes,
          pointmarkerlist != NULL ? 1 : 0);

  // Save segments or facets.
  if (mesh_dim == 2) {
    // Number of segments, number of boundary markers (zero or one).
    fprintf(fout, "%d  %d\n", numberofedges, edgemarkerlist != NULL ? 1 : 0);
    for (i = 0; i < numberofedges; i++) {
      fprintf(fout, "%d  %4d  %4d", i + firstnumber, edgelist[i * 2],
              edgelist[i * 2 + 1]);
      if (edgemarkerlist != NULL) {
        fprintf(fout, "  %d", edgemarkerlist[i]);
      }
      fprintf(fout, "\n");
    }
  } else {
    // Number of facets, number of boundary markers (zero or one).
    fprintf(fout, "%d  %d\n", numberoffacets, facetmarkerlist != NULL ? 1 : 0);
    for (i = 0; i < numberoffacets; i++) {
      f = &(facetlist[i]);
      fprintf(fout, "%d  %d  %d  # %d\n", f->numberofpolygons,f->numberofholes,
            facetmarkerlist != NULL ? facetmarkerlist[i] : 0, i + firstnumber);
      // Output polygons of this facet.
      for (j = 0; j < f->numberofpolygons; j++) {
        p = &(f->polygonlist[j]);
        fprintf(fout, "%d  ", p->numberofvertices);
        for (k = 0; k < p->numberofvertices; k++) {
          if (((k + 1) % 10) == 0) {
            fprintf(fout, "\n  ");
          }
          fprintf(fout, "  %d", p->vertexlist[k]);
        }
        fprintf(fout, "\n");
      }
      // Output holes of this facet.
      for (j = 0; j < f->numberofholes; j++) {
        fprintf(fout, "%d  %.12g  %.12g  %.12g\n", j + firstnumber,
           f->holelist[j * 3], f->holelist[j * 3 + 1], f->holelist[j * 3 + 2]);
      }
    }
  }

  // Save holes.
  fprintf(fout, "%d\n", numberofholes);
  for (i = 0; i < numberofholes; i++) {
    // Output x, y coordinates.
    fprintf(fout, "%d  %.12g  %.12g", i + firstnumber, holelist[i * mesh_dim],
            holelist[i * mesh_dim + 1]);
    if (mesh_dim == 3) {
      // Output z coordinate.
      fprintf(fout, "  %.12g", holelist[i * mesh_dim + 2]);
    }
    fprintf(fout, "\n");
  }

  // Save regions.
  fprintf(fout, "%d\n", numberofregions);
  for (i = 0; i < numberofregions; i++) {
    if (mesh_dim == 2) {
      // Output the index, x, y coordinates, attribute (region number)
      //   and maximum area constraint (maybe -1).
      fprintf(fout, "%d  %.12g  %.12g  %.12g  %.12g\n", i + firstnumber,
              regionlist[i * 4], regionlist[i * 4 + 1],
              regionlist[i * 4 + 2], regionlist[i * 4 + 3]);
    } else {
      // Output the index, x, y, z coordinates, attribute (region number)
      //   and maximum volume constraint (maybe -1).
      fprintf(fout, "%d  %.12g  %.12g  %.12g  %.12g  %.12g\n", i + firstnumber,
              regionlist[i * 5], regionlist[i * 5 + 1],
              regionlist[i * 5 + 2], regionlist[i * 5 + 3],
              regionlist[i * 5 + 4]);
    }
  }

  fclose(fout);  
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// readline()   Read a nonempty line from a file.                            //
//                                                                           //
// A line is considered "nonempty" if it contains something more than white  //
// spaces.  If a line is considered empty, it will be dropped and the next   //
// line will be read, this process ends until reaching the end-of-file or a  //
// non-empty line.  Return NULL if it is the end-of-file, otherwise, return  //
// a pointer to the first non-whitespace character of the line.              //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

char* tetgenio::readline(char *string, FILE *infile, int *linenumber)
{
  char *result;

  // Search for a non-empty line.
  do {
    result = fgets(string, INPUTLINESIZE - 1, infile);
    if (linenumber) (*linenumber)++;
    if (result == (char *) NULL) {
      return (char *) NULL;
    }
    // Skip white spaces.
    while ((*result == ' ') || (*result == '\t')) result++;
    // If it's end of line, read another line and try again.
  } while (*result == '\0');
  return result;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// findnextfield()   Find the next field of a string.                        //
//                                                                           //
// Jumps past the current field by searching for whitespace or a comma, then //
// jumps past the whitespace or the comma to find the next field.            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

char* tetgenio::findnextfield(char *string)
{
  char *result;

  result = string;
  // Skip the current field.  Stop upon reaching whitespace or a comma.
  while ((*result != '\0') && (*result != ' ') &&  (*result != '\t') && 
         (*result != ',') && (*result != ';')) {
    result++;
  }
  // Now skip the whitespace or the comma, stop at anything else that looks
  //   like a character, or the end of a line. 
  while ((*result == ' ') || (*result == '\t') || (*result == ',') ||
         (*result == ';')) {
    result++;
  }
  return result;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// readnumberline()   Read a nonempty number line from a file.               //
//                                                                           //
// A line is considered "nonempty" if it contains something that looks like  //
// a number.  Comments (prefaced by `#') are ignored.                        //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

char* tetgenio::readnumberline(char *string, FILE *infile, char *infilename)
{
  char *result;

  // Search for something that looks like a number.
  do {
    result = fgets(string, INPUTLINESIZE, infile);
    if (result == (char *) NULL) {
      if (infilename != (char *) NULL) {
        //printf("  Error:  Unexpected end of file in %s.\n", infilename);
        terminatetetgen(1);
      }
      return result;
    }
    // Skip anything that doesn't look like a number, a comment, 
    //   or the end of a line. 
    while ((*result != '\0') && (*result != '#')
           && (*result != '.') && (*result != '+') && (*result != '-')
           && ((*result < '0') || (*result > '9'))) {
      result++;
    }
    // If it's a comment or end of line, read another line and try again.
  } while ((*result == '#') || (*result == '\0'));
  return result;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// findnextnumber()   Find the next field of a number string.                //
//                                                                           //
// Jumps past the current field by searching for whitespace or a comma, then //
// jumps past the whitespace or the comma to find the next field that looks  //
// like a number.                                                            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

char* tetgenio::findnextnumber(char *string)
{
  char *result;

  result = string;
  // Skip the current field.  Stop upon reaching whitespace or a comma.
  while ((*result != '\0') && (*result != '#') && (*result != ' ') && 
         (*result != '\t') && (*result != ',')) {
    result++;
  }
  // Now skip the whitespace and anything else that doesn't look like a
  //   number, a comment, or the end of a line. 
  while ((*result != '\0') && (*result != '#')
         && (*result != '.') && (*result != '+') && (*result != '-')
         && ((*result < '0') || (*result > '9'))) {
    result++;
  }
  // Check for a comment (prefixed with `#').
  if (*result == '#') {
    *result = '\0';
  }
  return result;
}

////                                                                       ////
////                                                                       ////
//// io_cxx ///////////////////////////////////////////////////////////////////

//// behavior_cxx /////////////////////////////////////////////////////////////
////                                                                       ////
////                                                                       ////

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// syntax()    Print list of command line switches.                          //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenbehavior::syntax()
{
  //printf("  tetgen [-prq_a_AiMYS_T_dzo_fenvgGOJBNEFICQVh] input_file\n");
  //printf("    -p  Tetrahedralizes a piecewise linear complex (PLC).\n");
  //printf("    -r  Reconstructs a previously generated mesh.\n");
  //printf("    -q  Refines mesh (to improve mesh quality).\n");
  //printf("    -a  Applies a maximum tetrahedron volume constraint.\n");
  //printf("    -A  Assigns attributes to tetrahedra in different regions.\n");
  //printf("    -i  Inserts a list of additional points into mesh.\n");
  //printf("    -M  No merge of coplanar facets.\n");
  //printf("    -Y  No splitting of input boundaries (facets and segments).\n");
  //printf("    -S  Specifies maximum number of added points.\n");
  //printf("    -T  Sets a tolerance for coplanar test (default 1e-8).\n");
  //printf("    -d  Detects self-intersections of facets of the PLC.\n");
  //printf("    -z  Numbers all output items starting from zero.\n");
  //printf("    -o2 Generates second-order subparametric elements.\n");
  //printf("    -f  Outputs all faces to .face file.\n");
  //printf("    -e  Outputs all edges to .edge file.\n");
  //printf("    -n  Outputs tetrahedra neighbors to .neigh file.\n");
  //printf("    -v  Outputs Voronoi diagram to files.\n");
  //printf("    -g  Outputs mesh to .mesh file for viewing by Medit.\n");
  //printf("    -G  Outputs mesh to .msh file for viewing by Gid.\n");
  //printf("    -O  Outputs mesh to .off file for viewing by Geomview.\n");
  //printf("    -K  Outputs mesh to .vtk file for viewing by Paraview.\n");
  //printf("    -J  No jettison of unused vertices from output .node file.\n");
  //printf("    -B  Suppresses output of boundary information.\n");
  //printf("    -N  Suppresses output of .node file.\n");
  //printf("    -E  Suppresses output of .ele file.\n");
  //printf("    -F  Suppresses output of .face file.\n");
  //printf("    -I  Suppresses mesh iteration numbers.\n");
  //printf("    -C  Checks the consistency of the final mesh.\n");
  //printf("    -Q  Quiet:  No terminal output except errors.\n");
  //printf("    -V  Verbose:  Detailed information, more terminal output.\n");
  //printf("    -h  Help:  A brief instruction for using TetGen.\n");
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// usage()    Print a brief instruction for using TetGen.                    //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenbehavior::usage()
{
  //printf("TetGen\n");
  //printf("A Quality Tetrahedral Mesh Generator and 3D Delaunay ");
  //printf("Triangulator\n");
  //versioninfo();
  //printf("Version 1.4.3 (January 19, 2011).\n");
  //printf("\n");
  //printf("Copyright (C) 2002 - 2011\n");
  //printf("Hang Si\n");
  //printf("Mohrenstr. 39, 10117 Berlin, Germany\n");
  //printf("si@wias-berlin.de\n");
  //printf("\n");
  //printf("What Can TetGen Do?\n");
  //printf("\n");
  //printf("  TetGen generates exact Delaunay tetrahedralizations, exact\n");
  //printf("  constrained Delaunay tetrahedralizations, and quality ");
  //printf("tetrahedral\n  meshes. The latter are nicely graded and whose ");
  //printf("tetrahedra have\n  radius-edge ratio bounded, thus are suitable ");
  //printf("for finite element and\n  finite volume analysis.\n"); 
  //printf("\n");
  //printf("Command Line Syntax:\n");
  //printf("\n");
  //printf("  Below is the command line syntax of TetGen with a list of ");
  //printf("short\n");
  //printf("  descriptions. Underscores indicate that numbers may optionally\n");
  //printf("  follow certain switches.  Do not leave any space between a ");
  //printf("switch\n");
  //printf("  and its numeric parameter.  \'input_file\' contains input data\n");
  //printf("  depending on the switches you supplied which may be a ");
  //printf("  piecewise\n");
  //printf("  linear complex or a list of nodes.  File formats and detailed\n");
  //printf("  description of command line switches are found in user's ");
  //printf("manual.\n");
  //printf("\n");
  syntax();
  //printf("\n");
  //printf("Examples of How to Use TetGen:\n");
  //printf("\n");
  //printf("  \'tetgen object\' reads vertices from object.node, and writes ");
  //printf("their\n  Delaunay tetrahedralization to object.1.node and ");
  //printf("object.1.ele.\n");
  //printf("\n");
  //printf("  \'tetgen -p object\' reads a PLC from object.poly or object.");
  //printf("smesh (and\n  possibly object.node) and writes its constrained ");
  //printf("Delaunay\n  tetrahedralization to object.1.node, object.1.ele and ");
  //printf("object.1.face.\n");
  //printf("\n");
  //printf("  \'tetgen -pq1.414a.1 object\' reads a PLC from object.poly or\n");
  //printf("  object.smesh (and possibly object.node), generates a mesh ");
  //printf("whose\n  tetrahedra have radius-edge ratio smaller than 1.414 and ");
  //printf("have volume\n  of 0.1 or less, and writes the mesh to ");
  //printf("object.1.node, object.1.ele\n  and object.1.face.\n");
  //printf("\n");
  //printf("Please send bugs/comments to Hang Si <si@wias-berlin.de>\n");
  terminatetetgen(0);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// parse_commandline()    Read the command line, identify switches, and set  //
//                        up options and file names.                         //
//                                                                           //
// 'argc' and 'argv' are the same parameters passed to the function main()   //
// of a C/C++ program. They together represent the command line user invoked //
// from an environment in which TetGen is running.                           //
//                                                                           //
// When TetGen is invoked from an environment. 'argc' is nonzero, switches   //
// and input filename should be supplied as zero-terminated strings in       //
// argv[0] through argv[argc - 1] and argv[0] shall be the name used to      //
// invoke TetGen, i.e. "tetgen".  Switches are previously started with a     //
// dash '-' to identify them from the input filename.                        //
//                                                                           //
// When TetGen is called from within another program. 'argc' is set to zero. //
// switches are given in one zero-terminated string (no previous dash is     //
// required.), and 'argv' is a pointer points to this string.  No input      //
// filename is required (usually the input data has been directly created by //
// user in the 'tetgenio' structure).  A default filename 'tetgen-tmpfile'   //
// will be created for debugging output purpose.                             //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenbehavior::parse_commandline(int argc, char **argv)
{
  int startindex;
  int increment;
  int meshnumber;
  int scount;
  int i, j, k;
  char workstring[1024];

  // First determine the input style of the switches.
  if (argc == 0) {
    startindex = 0;                    // Switches are given without a dash.
    argc = 1;                    // For running the following for-loop once.
    commandline[0] = '\0';
  } else {
    startindex = 1;
    strcpy(commandline, argv[0]);
    strcat(commandline, " ");
  }
  
  // Rcount used to count the number of '-R' be used.
  scount = 0;

  for (i = startindex; i < argc; i++) {
    // Remember the command line switches.
    strcat(commandline, argv[i]);
    strcat(commandline, " ");
    if (startindex == 1) {
      // Is this string a filename?
      if (argv[i][0] != '-') {
        strncpy(infilename, argv[i], 1024 - 1);
        infilename[1024 - 1] = '\0';
        // Go to the next string directly.
        continue;                     
      }
    }
    // Parse the individual switch from the string.
    for (j = startindex; argv[i][j] != '\0'; j++) {
      if (argv[i][j] == 'p') {
        plc = 1;
      } else if (argv[i][j] == 'r') {
        refine++;
      } else if (argv[i][j] == 'R') {
        coarse = 1;
      } else if (argv[i][j] == 'q') {
        quality++;
        if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
            (argv[i][j + 1] == '.')) {
          k = 0;
          while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
                 (argv[i][j + 1] == '.')) {
            j++;
            workstring[k] = argv[i][j];
            k++;
          }
          workstring[k] = '\0';
          if (quality == 1) {
            minratio = (REAL) strtod(workstring, (char **) NULL);
          } else if (quality == 2) {
            mindihedral = (REAL) strtod(workstring, (char **) NULL);
          } else if (quality == 3) {
            maxdihedral = (REAL) strtod(workstring, (char **) NULL);
          }
        }
      } else if (argv[i][j] == 'm') {
        metric++;
        if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
            (argv[i][j + 1] == '.')) {
          k = 0;
          while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
                 (argv[i][j + 1] == '.')) {
            j++;
            workstring[k] = argv[i][j];
            k++;
          }
          workstring[k] = '\0';
          if (metric == 1) {
            alpha1 = (REAL) strtod(workstring, (char **) NULL);
          } else if (metric == 2) {
            alpha2 = (REAL) strtod(workstring, (char **) NULL);
          }
        }
      } else if (argv[i][j] == 'a') {
        if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
            (argv[i][j + 1] == '.')) {
          fixedvolume = 1;
          k = 0;
          while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
                 (argv[i][j + 1] == '.') || (argv[i][j + 1] == 'e') ||
                 (argv[i][j + 1] == '-') || (argv[i][j + 1] == '+')) {
            j++;
            workstring[k] = argv[i][j];
            k++;
          }
          workstring[k] = '\0';
          maxvolume = (REAL) strtod(workstring, (char **) NULL);
        } else {
          varvolume = 1;
        }
      } else if (argv[i][j] == 'A') {
        regionattrib++;
      } else if (argv[i][j] == 'u') {
        // Set the maximum btree node size, -u0 means do not use btree.
        if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
            (argv[i][j + 1] == '.')) {
          k = 0;
          while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
                 (argv[i][j + 1] == '.')) {
            j++;
            workstring[k] = argv[i][j];
            k++;
          }
          workstring[k] = '\0';
          max_btreenode_size = (int) strtol(workstring, (char **) NULL, 0);
        }
        if (max_btreenode_size == 0) {
          btree = 0;
        }
      } else if (argv[i][j] == 'i') {
        insertaddpoints = 1;
      } else if (argv[i][j] == 'd') {
        diagnose = 1;
      } else if (argv[i][j] == 'z') {
        zeroindex = 1;
      } else if (argv[i][j] == 'f') {
        facesout = 1;
      } else if (argv[i][j] == 'e') {
        edgesout++;
      } else if (argv[i][j] == 'n') {
        neighout++;
      } else if (argv[i][j] == 'v') {
        voroout = 1;
      } else if (argv[i][j] == 'g') {
        meditview = 1;
      } else if (argv[i][j] == 'G') {
        gidview = 1;
      } else if (argv[i][j] == 'O') {
        geomview = 1;
      } else if (argv[i][j] == 'K') {
        vtkview = 1;  
      } else if (argv[i][j] == 'M') {
        nomerge = 1;
      } else if (argv[i][j] == 'Y') {
        nobisect++;
      } else if (argv[i][j] == 'J') {
        nojettison = 1;
      } else if (argv[i][j] == 'B') {
        nobound = 1;
      } else if (argv[i][j] == 'N') {
        nonodewritten = 1;
      } else if (argv[i][j] == 'E') {
        noelewritten = 1;
        if (argv[i][j + 1] == '2') {
          j++;
          noelewritten = 2;
        }
      } else if (argv[i][j] == 'F') {
        nofacewritten = 1;
      } else if (argv[i][j] == 'I') {
        noiterationnum = 1;
      } else if (argv[i][j] == 'o') {
        if (argv[i][j + 1] == '2') {
          j++;
          order = 2;
        }
      } else if (argv[i][j] == 'S') {
        if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
            (argv[i][j + 1] == '.')) {
          k = 0;
          while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
                 (argv[i][j + 1] == '.') || (argv[i][j + 1] == 'e') ||
                 (argv[i][j + 1] == '-') || (argv[i][j + 1] == '+')) {
            j++;
            workstring[k] = argv[i][j];
            k++;
          }
          workstring[k] = '\0';
          steiner = (int) strtol(workstring, (char **) NULL, 0);
        } 
      } else if (argv[i][j] == 's') {
        scount++;
        if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
            (argv[i][j + 1] == '.')) {
          k = 0;
          while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
                 (argv[i][j + 1] == '.') || (argv[i][j + 1] == 'e') ||
                 (argv[i][j + 1] == '-') || (argv[i][j + 1] == '+')) {
            j++;
            workstring[k] = argv[i][j];
            k++;
          }
          workstring[k] = '\0';
          if (scount == 1) {
            optlevel = (int) strtol(workstring, (char **) NULL, 0);
          } else if (scount == 2) {
            optpasses = (int) strtol(workstring, (char **) NULL, 0);
          }
        }
      } else if (argv[i][j] == 'D') {
        conformdel++;
      } else if (argv[i][j] == 'T') {
        if (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
            (argv[i][j + 1] == '.')) {
          k = 0;
          while (((argv[i][j + 1] >= '0') && (argv[i][j + 1] <= '9')) ||
                 (argv[i][j + 1] == '.') || (argv[i][j + 1] == 'e') ||
                 (argv[i][j + 1] == '-') || (argv[i][j + 1] == '+')) {
            j++;
            workstring[k] = argv[i][j];
            k++;
          }
          workstring[k] = '\0';
          epsilon = (REAL) strtod(workstring, (char **) NULL);
        } 
      } else if (argv[i][j] == 'C') {
        docheck++;
      } else if (argv[i][j] == 'X') {
        fliprepair = 0;
      } else if (argv[i][j] == 'Q') {
        quiet = 1;
      } else if (argv[i][j] == 'V') {
        verbose++;
      } else if ((argv[i][j] == 'h') || (argv[i][j] == 'H') ||
                 (argv[i][j] == '?')) {
        usage();
      } else {
        //printf("Warning:  Unknown switch -%c.\n", argv[i][j]);
      }
    }
  }

  if (startindex == 0) {
    // Set a temporary filename for debugging output.
    strcpy(infilename, "tetgen-tmpfile");
  } else {
    if (infilename[0] == '\0') {
      // No input file name. Print the syntax and exit.
      syntax();
      terminatetetgen(0);
    }
    // Recognize the object from file extension if it is available.
    if (!strcmp(&infilename[strlen(infilename) - 5], ".node")) {
      infilename[strlen(infilename) - 5] = '\0';
      object = NODES;
    } else if (!strcmp(&infilename[strlen(infilename) - 5], ".poly")) {
      infilename[strlen(infilename) - 5] = '\0';
      object = POLY;
      plc = 1;
    } else if (!strcmp(&infilename[strlen(infilename) - 6], ".smesh")) {
      infilename[strlen(infilename) - 6] = '\0';
      object = POLY;
      plc = 1;
    } else if (!strcmp(&infilename[strlen(infilename) - 4], ".off")) {
      infilename[strlen(infilename) - 4] = '\0';
      object = OFF;
      plc = 1;
    } else if (!strcmp(&infilename[strlen(infilename) - 4], ".ply")) {
      infilename[strlen(infilename) - 4] = '\0';
      object = PLY;
      plc = 1;
    } else if (!strcmp(&infilename[strlen(infilename) - 4], ".stl")) {
      infilename[strlen(infilename) - 4] = '\0';
      object = STL;
      plc = 1;
    } else if (!strcmp(&infilename[strlen(infilename) - 5], ".mesh")) {
      infilename[strlen(infilename) - 5] = '\0';
      object = MEDIT;
      plc = 1;
    } else if (!strcmp(&infilename[strlen(infilename) - 4], ".vtk")) {
      infilename[strlen(infilename) - 4] = '\0';
      object = VTK;
      plc = 1;
    } else if (!strcmp(&infilename[strlen(infilename) - 4], ".ele")) {
      infilename[strlen(infilename) - 4] = '\0';
      object = MESH;
      refine = 1;
    }
  }
  plc = plc || diagnose;
  useshelles = plc || refine || coarse || quality;
  goodratio = minratio;
  goodratio *= goodratio;

  // Detect improper combinations of switches.
  if (plc && refine) {
    //printf("Error:  Switch -r cannot use together with -p.\n");
    return false;
  }
  if (refine && (plc || noiterationnum)) {
    //printf("Error:  Switches %s cannot use together with -r.\n",
           //"-p, -d, and -I");
    return false;
  }
  if (diagnose && (quality || insertaddpoints || (order == 2) || neighout
      || docheck)) {
    //printf("Error:  Switches %s cannot use together with -d.\n",
           //"-q, -i, -o2, -n, and -C");
    return false;
  }

  // Be careful not to allocate space for element area constraints that 
  //   will never be assigned any value (other than the default -1.0).
  if (!refine && !plc) {
    varvolume = 0;
  }
  // Be careful not to add an extra attribute to each element unless the
  //   input supports it (PLC in, but not refining a preexisting mesh).
  if (refine || !plc) {
    regionattrib = 0;
  }
  // If '-a' or '-aa' is in use, enable '-q' option too.
  if (fixedvolume || varvolume) {
    if (quality == 0) {
      quality = 1;
    }
  }
  // Calculate the goodangle for testing bad subfaces.
  goodangle = cos(minangle * tetgenmesh::PI / 180.0);
  goodangle *= goodangle;

  increment = 0;
  strcpy(workstring, infilename);
  j = 1;
  while (workstring[j] != '\0') {
    if ((workstring[j] == '.') && (workstring[j + 1] != '\0')) {
      increment = j + 1;
    }
    j++;
  }
  meshnumber = 0;
  if (increment > 0) {
    j = increment;
    do {
      if ((workstring[j] >= '0') && (workstring[j] <= '9')) {
        meshnumber = meshnumber * 10 + (int) (workstring[j] - '0');
      } else {
        increment = 0;
      }
      j++;
    } while (workstring[j] != '\0');
  }
  if (noiterationnum) {
    strcpy(outfilename, infilename);
  } else if (increment == 0) {
    strcpy(outfilename, infilename);
    strcat(outfilename, ".1");
  } else {
    workstring[increment] = '%';
    workstring[increment + 1] = 'd';
    workstring[increment + 2] = '\0';
    sprintf(outfilename, workstring, meshnumber + 1);
  }
  // Additional input file name has the end ".a".
  strcpy(addinfilename, infilename);
  strcat(addinfilename, ".a");
  // Background filename has the form "*.b.ele", "*.b.node", ...
  strcpy(bgmeshfilename, infilename);
  strcat(bgmeshfilename, ".b");

  return true;
}

////                                                                       ////
////                                                                       ////
//// behavior_cxx /////////////////////////////////////////////////////////////

//// prim_cxx /////////////////////////////////////////////////////////////////
////                                                                       ////
////                                                                       ////

// For enumerating three edges of a triangle.

int tetgenmesh::plus1mod3[3] = {1, 2, 0};
int tetgenmesh::minus1mod3[3] = {2, 0, 1};

// Table 've' takes an edge version as input, returns the next edge version
//   in the same edge ring.

int tetgenmesh::ve[6] = { 2, 5, 4, 1, 0, 3 };

// Tables 'vo', 'vd' and 'va' take an edge version, return the positions of
//   the origin, destination and apex in the triangle.

int tetgenmesh::vo[6] = { 0, 1, 1, 2, 2, 0 };
int tetgenmesh::vd[6] = { 1, 0, 2, 1, 0, 2 };
int tetgenmesh::va[6] = { 2, 2, 0, 0, 1, 1 };

// The following tables are for tetrahedron primitives (operate on trifaces).

// For 'org()', 'dest()' and 'apex()'.  Use 'loc' as the first index and
//   'ver' as the second index.

int tetgenmesh::locver2org[4][6]  = {
  {0, 1, 1, 2, 2, 0},
  {0, 3, 3, 1, 1, 0},
  {1, 3, 3, 2, 2, 1},
  {2, 3, 3, 0, 0, 2} 
};
int tetgenmesh::locver2dest[4][6] = { 
  {1, 0, 2, 1, 0, 2},
  {3, 0, 1, 3, 0, 1},
  {3, 1, 2, 3, 1, 2},
  {3, 2, 0, 3, 2, 0}
};
int tetgenmesh::locver2apex[4][6] = { 
  {2, 2, 0, 0, 1, 1},
  {1, 1, 0, 0, 3, 3},
  {2, 2, 1, 1, 3, 3},
  {0, 0, 2, 2, 3, 3}
};

// For oppo() primitives, use 'loc' as the index.

int tetgenmesh::loc2oppo[4] = { 3, 2, 0, 1 };

// For fnext() primitive.  Use 'loc' as the first index and 'ver' as the
//   second index. Returns a new 'loc' and new 'ver' in an array. (It is
//   only valid for edge version equals one of {0, 2, 4}.)

int tetgenmesh::locver2nextf[4][6][2] = {
  { {1, 5}, {-1, -1}, {2, 5}, {-1, -1}, {3, 5}, {-1, -1} },
  { {3, 3}, {-1, -1}, {2, 1}, {-1, -1}, {0, 1}, {-1, -1} },
  { {1, 3}, {-1, -1}, {3, 1}, {-1, -1}, {0, 3}, {-1, -1} },
  { {2, 3}, {-1, -1}, {1, 1}, {-1, -1}, {0, 5}, {-1, -1} }
};

// The edge number (from 0 to 5) of a tet is defined as follows:
//   0 - (v0, v1), 1 - (v1, v2), 2 - (v2, v0)
//   3 - (v3, v0), 4 - (v3, v1), 5 - (v3, v2).

int tetgenmesh::locver2edge[4][6] = {
  {0, 0, 1, 1, 2, 2},
  {3, 3, 4, 4, 0, 0},
  {4, 4, 5, 5, 1, 1},
  {5, 5, 3, 3, 2, 2}
};

int tetgenmesh::edge2locver[6][2] = {
  {0, 0}, // 0  v0 -> v1 (a -> b)
  {0, 2}, // 1  v1 -> v2 (b -> c) 
  {0, 4}, // 2  v2 -> v0 (c -> a)
  {1, 0}, // 3  v0 -> v3 (a -> d)
  {1, 2}, // 4  v1 -> v3 (b -> d
  {2, 2}  // 5  v2 -> v3 (c -> d)
};

int tetgenmesh::locpivot[4][3] = {
  {1, 2, 3},
  {0, 2, 3},
  {0, 1, 3},
  {0, 1, 2}
};

int tetgenmesh::locverpivot[4][6][2] = {
  {{2, 3}, {2, 3}, {1, 3}, {1, 3}, {1, 2}, {1, 2}},
  {{0, 2}, {0, 2}, {0, 3}, {0, 3}, {2, 3}, {2, 3}},
  {{0, 3}, {0, 3}, {0, 1}, {0, 1}, {1, 3}, {1, 3}},
  {{0, 1}, {0, 1}, {0, 2}, {0, 2}, {1, 2}, {1, 2}}
};

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// getnextsface()    Finds the next subface in the face ring.                //
//                                                                           //
// For saving space in the data structure of subface, there only exists one  //
// face ring around a segment (see programming manual).  This routine imple- //
// ments the double face ring as desired in Muecke's data structure.         //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::getnextsface(face* s1, face* s2)
{
  face neighsh, spinsh;
  face testseg;

  sspivot(*s1, testseg);
  if (testseg.sh != dummysh) {
    testseg.shver = 0;
    if (sorg(testseg) == sorg(*s1)) {
      spivot(*s1, neighsh);
    } else {
      spinsh = *s1;
      do {
        neighsh = spinsh;
        spivotself(spinsh);
      } while (spinsh.sh != s1->sh);
    }
  } else {
    spivot(*s1, neighsh);
  }
  if (sorg(neighsh) != sorg(*s1)) {
    sesymself(neighsh);
  }
  if (s2 != (face *) NULL) {
    *s2 = neighsh;
  } else {
    *s1 = neighsh;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// tsspivot()    Finds a subsegment abutting on a tetrahderon's edge.        //
//                                                                           //
// The edge is represented in the primary edge of 'checkedge'. If there is a //
// subsegment bonded at this edge, it is returned in handle 'checkseg', the  //
// edge direction of 'checkseg' is conformed to 'checkedge'. If there isn't, //
// set 'checkseg.sh = dummysh' to indicate it is not a subsegment.           //
//                                                                           //
// To find whether an edge of a tetrahedron is a subsegment or not. First we //
// need find a subface around this edge to see if it contains a subsegment.  //
// The reason is there is no direct connection between a tetrahedron and its //
// adjoining subsegments.                                                    //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::tsspivot(triface* checkedge, face* checkseg)
{
  triface spintet;
  face parentsh;
  point tapex;
  int hitbdry;

  spintet = *checkedge;
  tapex = apex(*checkedge);
  hitbdry = 0;
  do {
    tspivot(spintet, parentsh);
    // Does spintet have a (non-fake) subface attached? 
    if ((parentsh.sh != dummysh) && (sapex(parentsh) != NULL)) {
      // Find a subface! Find the edge in it.      
      findedge(&parentsh, org(*checkedge), dest(*checkedge));
      sspivot(parentsh, *checkseg);
      if (checkseg->sh != dummysh) {
        // Find a subsegment! Correct its edge direction before return.
        if (sorg(*checkseg) != org(*checkedge)) {
          sesymself(*checkseg);
        }
      }
      return;
    }
    if (!fnextself(spintet)) {
      hitbdry++;
      if (hitbdry < 2) {
        esym(*checkedge, spintet);
        if (!fnextself(spintet)) {
          hitbdry++;
        }
      }
    }
  } while ((apex(spintet) != tapex) && (hitbdry < 2));
  // Not find.
  checkseg->sh = dummysh;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// sstpivot()    Finds a tetrahedron abutting a subsegment.                  //
//                                                                           //
// This is the inverse operation of 'tsspivot()'.  One subsegment shared by  //
// arbitrary number of tetrahedron, the returned tetrahedron is not unique.  //
// The edge direction of the returned tetrahedron is conformed to the given  //
// subsegment.                                                               //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::sstpivot(face* checkseg, triface* retedge)
{
  face parentsh;

  // Get the subface which holds the subsegment.
  sdecode(checkseg->sh[0], parentsh);
#ifdef SELF_CHECK
  //assert(parentsh.sh != dummysh);
#endif
  // Get a tetraheron to which the subface attches.
  stpivot(parentsh, *retedge);
  if (retedge->tet == dummytet) {
    sesymself(parentsh);
    stpivot(parentsh, *retedge);
#ifdef SELF_CHECK
    //assert(retedge->tet != dummytet);
#endif
  }
  // Correct the edge direction before return.
  findedge(retedge, sorg(*checkseg), sdest(*checkseg));
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// point2tetorg(), point2shorg(), point2segorg()                             //
//                                                                           //
// Return a tet, a subface, or a subsegment whose origin is the given point. //
// These routines assume the maps between points to tets (subfaces, segments //
// ) have been built and maintained.                                         //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::point2tetorg(point pa, triface& searchtet)
{
  int i;

  // Search a tet whose origin is pa.
  decode(point2tet(pa), searchtet);
  if (searchtet.tet == NULL) {
    /*//printf("Internal error: %d contains bad tet pointer.\n", pointmark(pa));
    terminatetetgen(2);*/
	  return;
  }
  for (i = 4; i < 8; i++) {
    if ((point) searchtet.tet[i] == pa) {
      // Found. Set pa as its origin.
      switch (i) {
        case 4: searchtet.loc = 0; searchtet.ver = 0; break;
        case 5: searchtet.loc = 0; searchtet.ver = 2; break;
        case 6: searchtet.loc = 0; searchtet.ver = 4; break;
        case 7: searchtet.loc = 1; searchtet.ver = 2; break;
      }
      break;
    }
  }
  if (i == 8) {
   /* //printf("Internal error: %d contains bad tet pointer.\n", pointmark(pa));
    terminatetetgen(2);*/
	  return;
  }
}

void tetgenmesh::point2shorg(point pa, face& searchsh)
{
  sdecode(point2sh(pa), searchsh);
  if (searchsh.sh == NULL) {
    //printf("Internal error: %d contains bad sub pointer.\n", pointmark(pa));
    //terminatetetgen(2);
	  return;
  }
  if (((point) searchsh.sh[3]) == pa) {
    searchsh.shver = 0;
  } else if (((point) searchsh.sh[4]) == pa) {
    searchsh.shver = 2;
  } else if (((point) searchsh.sh[5]) == pa) {
    searchsh.shver = 4;
  } else {
    //printf("Internal error: %d contains bad sub pointer.\n", pointmark(pa));
    //terminatetetgen(2);
	  return;
  }
}

void tetgenmesh::point2segorg(point pa, face& searchsh)
{
  sdecode(point2seg(pa), searchsh);
  if (searchsh.sh == NULL) {
    //printf("Internal error: %d contains bad seg pointer.\n", pointmark(pa));
    //terminatetetgen(2);
	  return;
  }
  if (((point) searchsh.sh[3]) == pa) {
    searchsh.shver = 0;
  } else if (((point) searchsh.sh[4]) == pa) {
    searchsh.shver = 1;
  } else {
    //printf("Internal error: %d contains bad sub pointer.\n", pointmark(pa));
    //terminatetetgen(2);
	  return;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// findorg()    Find a point in the given tet or subface.                    //
//                                                                           //
// If 'dorg' is a one of vertices of the given handle,  set the origin of    //
// this handle be that point and return TRUE.  Otherwise, return FALSE and   //
// 'tface' remains unchanged.                                                //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::findorg(triface* tface, point dorg)
{
  if (org(*tface) == dorg) {
    return true;
  } else { 
    if (dest(*tface) == dorg) {
      enextself(*tface);
      return true;
    } else {
      if (apex(*tface) == dorg) {
        enext2self(*tface);
        return true;
      } else {
        if (oppo(*tface) == dorg) {
          // Keep 'tface' referring to the same tet after fnext().
          adjustedgering(*tface, CCW);
          fnextself(*tface);
          enext2self(*tface);
          return true;
        } 
      }
    }
  }
  return false;
}

bool tetgenmesh::findorg(face* sface, point dorg)
{
  if (sorg(*sface) == dorg) {
    return true;
  } else {
    if (sdest(*sface) == dorg) {
      senextself(*sface);
      return true;
    } else {
      if (sapex(*sface) == dorg) {
        senext2self(*sface);
        return true;
      } 
    }
  }
  return false;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// findedge()    Find an edge in the given tet or subface.                   //
//                                                                           //
// The edge is given in two points 'eorg' and 'edest'.  It is assumed that   //
// the edge must exist in the given handle (tetrahedron or subface).  This   //
// routine sets the right edge version for the input handle.                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::findedge(triface* tface, point eorg, point edest)
{
  int i;

  for (i = 0; i < 3; i++) {
    if (org(*tface) == eorg) {
      if (dest(*tface) == edest) {
        // Edge is found, return.
        return;
      } 
    } else {
      if (org(*tface) == edest) {
        if (dest(*tface) == eorg) {
          // Edge is found, inverse the direction and return.
          esymself(*tface);
          return;
        }
      }
    }
    enextself(*tface);
  }
  // It should never be here.
  //printf("Internalerror in findedge():  Unable to find an edge in tet.\n");
  //terminatetetgen(2);
  return;
}

void tetgenmesh::findedge(face* sface, point eorg, point edest)
{
  int i;

  for (i = 0; i < 3; i++) {
    if (sorg(*sface) == eorg) {
      if (sdest(*sface) == edest) {
        // Edge is found, return.
        return;
      } 
    } else {
      if (sorg(*sface) == edest) {
        if (sdest(*sface) == eorg) {
          // Edge is found, inverse the direction and return.
          sesymself(*sface);
          return;
        }
      }
    }
    senextself(*sface);
  }
  //printf("Internalerror in findedge():  Unable to find an edge in subface.\n");
  return;
  //terminatetetgen(2);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// getonextseg()    Get the next segment counterclockwise with the same org. //
//                                                                           //
// 's' is a subface. This routine reteuns the segment which is counterclock- //
// wise with the origin of s.                                                //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::getonextseg(face* s, face* lseg)
{
  face checksh, checkseg;
  point forg;

  forg = sorg(*s);
  checksh = *s;
  do {
    // Go to the edge at forg's left side.
    senext2self(checksh);
    // Check if there is a segment attaching this edge.
    sspivot(checksh, checkseg);
    if (checkseg.sh != dummysh) break;
    // No segment! Go to the neighbor of this subface.
    spivotself(checksh);
#ifdef SELF_CHECK
    // It should always meet a segment before come back.
    //assert(checksh.sh != s->sh);
#endif
    if (sorg(checksh) != forg) {
      sesymself(checksh);
#ifdef SELF_CHECK
      //assert(sorg(checksh) == forg);
#endif
    }
  } while (true);
  if (sorg(checkseg) != forg) sesymself(checkseg);
  *lseg = checkseg;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// getseghasorg()    Get the segment containing the given point.             //
//                                                                           //
// 'dorg' is an endpoint of a segment S. 'sseg' is a subsegment of S. This   //
// routine search a subsegment (along sseg) of S containing dorg. On return, //
// 'sseg' contains 'dorg' as its origin.                                     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::getseghasorg(face* sseg, point dorg)
{
  face nextseg;
  point checkpt;

  nextseg = *sseg;
  checkpt = sorg(nextseg);
  while ((checkpt != dorg) && (pointtype(checkpt) == FREESEGVERTEX)) {
    // Search dorg along the original direction of sseg.
    senext2self(nextseg);
    spivotself(nextseg);
    nextseg.shver = 0;
    if (sdest(nextseg) != checkpt) sesymself(nextseg);
    checkpt = sorg(nextseg);
  }
  if (checkpt == dorg) {
    *sseg = nextseg;
    return;
  }
  nextseg = *sseg;
  checkpt = sdest(nextseg);
  while ((checkpt != dorg) && (pointtype(checkpt) == FREESEGVERTEX)) {
    // Search dorg along the destinational direction of sseg.
    senextself(nextseg);
    spivotself(nextseg);
    nextseg.shver = 0;
    if (sorg(nextseg) != checkpt) sesymself(nextseg);
    checkpt = sdest(nextseg);
  }
  if (checkpt == dorg) {
    sesym(nextseg, *sseg);
    return;
  }
  // Should never be here.
  //printf("Internalerror in getseghasorg():  Unable to find the subseg.\n");
  return;
  //terminatetetgen(2);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// getsubsegfarorg()    Get the origin of the parent segment of a subseg.    //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

tetgenmesh::point tetgenmesh::getsubsegfarorg(face* sseg)
{
  face prevseg;
  point checkpt;

  checkpt = sorg(*sseg);
  senext2(*sseg, prevseg);
  spivotself(prevseg);
  // Search dorg along the original direction of sseg.
  while (prevseg.sh != dummysh) {
    prevseg.shver = 0;
    if (sdest(prevseg) != checkpt) sesymself(prevseg);
    checkpt = sorg(prevseg);
    senext2self(prevseg);
    spivotself(prevseg);
  }
  return checkpt;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// getsubsegfardest()    Get the dest. of the parent segment of a subseg.    //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

tetgenmesh::point tetgenmesh::getsubsegfardest(face* sseg)
{
  face nextseg;
  point checkpt;

  checkpt = sdest(*sseg);
  senext(*sseg, nextseg);
  spivotself(nextseg);
  // Search dorg along the destinational direction of sseg.
  while (nextseg.sh != dummysh) {
    nextseg.shver = 0;
    if (sorg(nextseg) != checkpt) sesymself(nextseg);
    checkpt = sdest(nextseg);
    senextself(nextseg);
    spivotself(nextseg);
  }
  return checkpt;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// printtet()    Print out the details of a tetrahedron on screen.           //
//                                                                           //
// It's also used when the highest level of verbosity (`-VVV') is specified. //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::printtet(triface* tface)
{
  triface tmpface, prtface;
  shellface *shells;
  point tmppt;
  face checksh;
  int facecount;

  //printf("Tetra x%lx with loc(%i) and ver(%i):",
         //(uintptr_t)(tface->tet), tface->loc, tface->ver);
  if (infected(*tface)) {
    //printf(" (infected)");
  }
  if (marktested(*tface)) {
    //printf(" (marked)");
  }
  //printf("\n");

  tmpface = *tface;
  facecount = 0;
  while(facecount < 4) {
    tmpface.loc = facecount;
    sym(tmpface, prtface);
    if(prtface.tet == dummytet) {
      //printf("      [%i] Outer space.\n", facecount);
    } else {
      if (!isdead(&prtface)) {
        //printf("      [%i] x%lx  loc(%i).", facecount,
            //   (uintptr_t)(prtface.tet), prtface.loc);
        if (infected(prtface)) {
          //printf(" (infected)");
        }
        //printf("\n");
      } else {
        //printf("      [%i] NULL\n", facecount);
      }
    }
    facecount ++;
  }

  tmppt = org(*tface);
  if(tmppt == (point) NULL) {
    //printf("      Org [%i] NULL\n", locver2org[tface->loc][tface->ver]);
  } else {
    //printf("      Org [%i] x%lx (%.12g,%.12g,%.12g) %d\n",
           //locver2org[tface->loc][tface->ver], (uintptr_t)(tmppt),
           //tmppt[0], tmppt[1], tmppt[2], pointmark(tmppt));
  }
  tmppt = dest(*tface);
  if(tmppt == (point) NULL) {
    //printf("      Dest[%i] NULL\n", locver2dest[tface->loc][tface->ver]);
  } else {
    //printf("      Dest[%i] x%lx (%.12g,%.12g,%.12g) %d\n",
           //locver2dest[tface->loc][tface->ver], (uintptr_t)(tmppt),
           //tmppt[0], tmppt[1], tmppt[2], pointmark(tmppt));
  }
  tmppt = apex(*tface);
  if(tmppt == (point) NULL) {
    //printf("      Apex[%i] NULL\n", locver2apex[tface->loc][tface->ver]);
  } else {
    //printf("      Apex[%i] x%lx (%.12g,%.12g,%.12g) %d\n",
          // locver2apex[tface->loc][tface->ver], (uintptr_t)(tmppt),
           //tmppt[0], tmppt[1], tmppt[2], pointmark(tmppt));
  }
  tmppt = oppo(*tface);
  if(tmppt == (point) NULL) {
    //printf("      Oppo[%i] NULL\n", loc2oppo[tface->loc]);
  } else {
    //printf("      Oppo[%i] x%lx (%.12g,%.12g,%.12g) %d\n",
          // loc2oppo[tface->loc], (uintptr_t)(tmppt),
          // tmppt[0], tmppt[1], tmppt[2], pointmark(tmppt));
  }

  if (b->useshelles) {
    if (tface->tet[8] != NULL) {
      shells = (shellface *) tface->tet[8];
      for (facecount = 0; facecount < 6; facecount++) {
        sdecode(shells[facecount], checksh);
        if (checksh.sh != dummysh) {
          //printf("      [%d] x%lx %d.", facecount, (uintptr_t) checksh.sh,
            //checksh.shver);
        } else {
          //printf("      [%d] NULL.", facecount);
        }
        if (locver2edge[tface->loc][tface->ver] == facecount) {
          //printf(" (*)");  // It is the current edge.
        }
        //printf("\n");
      }
    }
    if (tface->tet[9] != NULL) {
      shells = (shellface *) tface->tet[9];
      for (facecount = 0; facecount < 4; facecount++) {
        sdecode(shells[facecount], checksh);
        if (checksh.sh != dummysh) {
          //printf("      [%d] x%lx %d.", facecount, (uintptr_t) checksh.sh,
            //checksh.shver);
        } else {
          //printf("      [%d] NULL.", facecount);
        }
        if (tface->loc == facecount) {
          //printf(" (*)");  // It is the current face.
        }
        //printf("\n");
      }
    }
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// printsh()    Print out the details of a subface or subsegment on screen.  //
//                                                                           //
// It's also used when the highest level of verbosity (`-VVV') is specified. //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::printsh(face* sface)
{
  face prtsh;
  triface prttet;
  point printpoint;

  if (sapex(*sface) != NULL) {
    //printf("subface x%lx, ver %d, mark %d:",
          // (uintptr_t)(sface->sh), sface->shver, shellmark(*sface));
  } else {
    //printf("Subsegment x%lx, ver %d, mark %d:",
           //(uintptr_t)(sface->sh), sface->shver, shellmark(*sface));
  }
  if (sinfected(*sface)) {
    //printf(" (infected)");
  }
  if (smarktested(*sface)) {
    //printf(" (marked)");
  }
  if (shell2badface(*sface)) {
    //printf(" (queued)");
  }
  if (sapex(*sface) != NULL) {
    if (shelltype(*sface) == SHARP) {
      //printf(" (sharp)");
    }
  } else {
    if (shelltype(*sface) == SHARP) {
      //printf(" (sharp)");
    }
  }
  if (checkpbcs) {
    if (shellpbcgroup(*sface) >= 0) {
      //printf(" (pbc %d)", shellpbcgroup(*sface));
    }
  }
  //printf("\n");

  sdecode(sface->sh[0], prtsh);
  if (prtsh.sh == dummysh) {
    //printf("      [0] = No shell\n");
  } else {
    //printf("      [0] = x%lx  %d\n", (uintptr_t)(prtsh.sh), prtsh.shver);
  }
  sdecode(sface->sh[1], prtsh);
  if (prtsh.sh == dummysh) {
    //printf("      [1] = No shell\n");
  } else {
    //printf("      [1] = x%lx  %d\n", (uintptr_t)(prtsh.sh), prtsh.shver);
  }
  sdecode(sface->sh[2], prtsh);
  if (prtsh.sh == dummysh) {
    //printf("      [2] = No shell\n");
  } else {
    //printf("      [2] = x%lx  %d\n", (uintptr_t)(prtsh.sh), prtsh.shver);
  }

  printpoint = sorg(*sface);
  if (printpoint == (point) NULL) {
    //printf("      Org [%d] = NULL\n", vo[sface->shver]);
  }
  else {
    //printf("      Org [%d] = x%lx  (%.12g,%.12g,%.12g) %d\n",
          // vo[sface->shver], (uintptr_t)(printpoint), printpoint[0],
           //printpoint[1], printpoint[2], pointmark(printpoint));
  }
  printpoint = sdest(*sface);
  if (printpoint == (point) NULL) {
    //printf("      Dest[%d] = NULL\n", vd[sface->shver]);
  }
  else {
    //printf("      Dest[%d] = x%lx  (%.12g,%.12g,%.12g) %d\n",
            //vd[sface->shver], (uintptr_t)(printpoint), printpoint[0],
            //printpoint[1], printpoint[2], pointmark(printpoint));
  }

  if (sapex(*sface) != NULL) {
    printpoint = sapex(*sface);
	if (printpoint == (point) NULL) {
      //printf("      Apex[%d] = NULL\n", va[sface->shver]);
	}
	else {
      //printf("      Apex[%d] = x%lx  (%.12g,%.12g,%.12g) %d\n",
             //va[sface->shver], (uintptr_t)(printpoint), printpoint[0],
             //printpoint[1], printpoint[2], pointmark(printpoint));
	}

    decode(sface->sh[6], prttet);
    if (prttet.tet == dummytet) {
      //printf("      [6] = Outer space\n");
    } else {
      //printf("      [6] = x%lx  %d\n",
            // (uintptr_t)(prttet.tet), prttet.loc);
    }
    decode(sface->sh[7], prttet);
    if (prttet.tet == dummytet) {
      //printf("      [7] = Outer space\n");
    } else {
      //printf("      [7] = x%lx  %d\n",
             //(uintptr_t)(prttet.tet), prttet.loc);
    }

    sdecode(sface->sh[8], prtsh);
    if (prtsh.sh == dummysh) {
      //printf("      [8] = No subsegment\n");
    } else {
      //printf("      [8] = x%lx  %d\n",
             //(uintptr_t)(prtsh.sh), prtsh.shver);
    }
    sdecode(sface->sh[9], prtsh);
    if (prtsh.sh == dummysh) {
      //printf("      [9] = No subsegment\n");
    } else {
      //printf("      [9] = x%lx  %d\n",
             //(uintptr_t)(prtsh.sh), prtsh.shver);
    }
    sdecode(sface->sh[10], prtsh);
    if (prtsh.sh == dummysh) {
      //printf("      [10]= No subsegment\n");
    } else {
      //printf("      [10]= x%lx  %d\n",
             //(uintptr_t)(prtsh.sh), prtsh.shver);
    }
  } 
}

////                                                                       ////
////                                                                       ////
//// prim_cxx /////////////////////////////////////////////////////////////////

//// mempool_cxx //////////////////////////////////////////////////////////////
////                                                                       ////
////                                                                       ////

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// restart()    Deallocate all objects in this pool.                         //
//                                                                           //
// The pool returns to a fresh state, like after it was initialized, except  //
// that no memory is freed to the operating system.  Rather, the previously  //
// allocated blocks are ready to be used.                                    //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::arraypool::restart()
{
  objects = 0l;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// poolinit()    Initialize an arraypool for allocation of objects.          //
//                                                                           //
// Before the pool may be used, it must be initialized by this procedure.    //
// After initialization, memory can be allocated and freed in this pool.     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::arraypool::poolinit(int sizeofobject, int log2objperblk)
{
  // Each object must be at least one byte long.
  objectbytes = sizeofobject > 1 ? sizeofobject : 1;

  log2objectsperblock = log2objperblk;
  // Compute the number of objects in each block.
  objectsperblock = ((int) 1) << log2objectsperblock;

  // No memory has been allocated.
  totalmemory = 0l;
  // The top array has not been allocated yet.
  toparray = (char **) NULL;
  toparraylen = 0;

  // Ready all indices to be allocated.
  restart();
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// arraypool()    The constructor and destructor.                            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

tetgenmesh::arraypool::arraypool(int sizeofobject, int log2objperblk)
{
  poolinit(sizeofobject, log2objperblk);
}

tetgenmesh::arraypool::~arraypool()
{
  int i;

  // Has anything been allocated at all?
  if (toparray != (char **) NULL) {
    // Walk through the top array.
    for (i = 0; i < toparraylen; i++) {
      // Check every pointer; NULLs may be scattered randomly.
      if (toparray[i] != (char *) NULL) {
        // Free an allocated block.
        free((void *) toparray[i]);
      }
    }
    // Free the top array.
    free((void *) toparray);
  }

  // The top array is no longer allocated.
  toparray = (char **) NULL;
  toparraylen = 0;
  objects = 0;
  totalmemory = 0;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// getblock()    Return (and perhaps create) the block containing the object //
//               with a given index.                                         //
//                                                                           //
// This function takes care of allocating or resizing the top array if nece- //
// ssary, and of allocating the block if it hasn't yet been allocated.       //
//                                                                           //
// Return a pointer to the beginning of the block (NOT the object).          //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

char* tetgenmesh::arraypool::getblock(int objectindex)
{
  char **newarray;
  char *block;
  int newsize;
  int topindex;
  int i;

  // Compute the index in the top array (upper bits).
  topindex = objectindex >> log2objectsperblock;
  // Does the top array need to be allocated or resized?
  if (toparray == (char **) NULL) {
    // Allocate the top array big enough to hold 'topindex', and NULL out
    //   its contents.
    newsize = topindex + 128;
    //marktag
	//toparray = (char **) malloc((size_t) (newsize * sizeof(char *)));
    toparraylen = newsize;
    for (i = 0; i < newsize; i++) {
      toparray[i] = (char *) NULL;
    }
    // Account for the memory.
    totalmemory = newsize * (unsigned long) sizeof(char *);
  } else if (topindex >= toparraylen) {
    // Resize the top array, making sure it holds 'topindex'.
    newsize = 3 * toparraylen;
    if (topindex >= newsize) {
      newsize = topindex + 128;
    }
    // Allocate the new array, copy the contents, NULL out the rest, and
    //   free the old array.
    //marktag
	//newarray = (char **) malloc((size_t) (newsize * sizeof(char *)));
    for (i = 0; i < toparraylen; i++) {
      newarray[i] = toparray[i];
    }
    for (i = toparraylen; i < newsize; i++) {
      newarray[i] = (char *) NULL;
    }
	//marktag
    //free(toparray);
    // Account for the memory.
    totalmemory += (newsize - toparraylen) * sizeof(char *);
    toparray = newarray;
    toparraylen = newsize;
  }

  // Find the block, or learn that it hasn't been allocated yet.
  block = toparray[topindex];
  if (block == (char *) NULL) {
    // Allocate a block at this index.
    //marktag
	  //block = (char *) malloc((size_t) (objectsperblock * objectbytes));
    toparray[topindex] = block;
    // Account for the memory.
    totalmemory += objectsperblock * objectbytes;
  }

  // Return a pointer to the block.
  return block;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// lookup()    Return the pointer to the object with a given index, or NULL  //
//             if the object's block doesn't exist yet.                      //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void* tetgenmesh::arraypool::lookup(int objectindex)
{
  char *block;
  int topindex;

  // Has the top array been allocated yet?
  if (toparray == (char **) NULL) {
    return (void *) NULL;
  }

  // Compute the index in the top array (upper bits).
  topindex = objectindex >> log2objectsperblock;
  // Does the top index fit in the top array?
  if (topindex >= toparraylen) {
    return (void *) NULL;
  }

  // Find the block, or learn that it hasn't been allocated yet.
  block = toparray[topindex];
  if (block == (char *) NULL) {
    return (void *) NULL;
  }

  // Compute a pointer to the object with the given index.  Note that
  //   'objectsperblock' is a power of two, so the & operation is a bit mask
  //   that preserves the lower bits.
  return (void *)(block + (objectindex & (objectsperblock - 1)) * objectbytes);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// newindex()    Allocate space for a fresh object from the pool.            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::arraypool::newindex(void **newptr)
{
  void *newobject;
  int newindex;

  // Allocate an object at index 'firstvirgin'.
  newindex = objects;
  newobject = (void *) (getblock(objects) +
    (objects & (objectsperblock - 1)) * objectbytes);
  objects++;

  // If 'newptr' is not NULL, use it to return a pointer to the object.
  if (newptr != (void **) NULL) {
    *newptr = newobject;
  }
  return newindex;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// listinit()    Initialize a list for storing a data type.                  //
//                                                                           //
// Determine the size of each item, set the maximum size allocated at onece, //
// set the expand size in case the list is full, and set the linear order    //
// function if it is provided (default is NULL).                             //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::list::listinit(int itbytes, compfunc pcomp, int mitems,
  int exsize)
{
  itembytes = itbytes;
  comp = pcomp;
  maxitems = mitems;
  expandsize = exsize;
  base = (char *) malloc(maxitems * itembytes); 
  if (base == (char *) NULL) {
    terminatetetgen(1);
  }
  items = 0;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// append()    Add a new item at the end of the list.                        //
//                                                                           //
// A new space at the end of this list will be allocated for storing the new //
// item. If the memory is not sufficient, reallocation will be performed. If //
// 'appitem' is not NULL, the contents of this pointer will be copied to the //
// new allocated space.  Returns the pointer to the new allocated space.     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void* tetgenmesh::list::append(void *appitem)
{
  // Do we have enough space?
  if (items == maxitems) {
	  //marktag
  //  char* newbase = (char *) realloc(base, (maxitems + expandsize) * 
  //                                   itembytes);
  //  if (newbase == (char *) NULL) {
  //    return;
		////terminatetetgen(1);
  //  }
  //  base = newbase;
  //  maxitems += expandsize;
  }
  if (appitem != (void *) NULL) {
    memcpy(base + items * itembytes, appitem, itembytes);
  }
  items++;
  return (void *) (base + (items - 1) * itembytes);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// insert()    Insert an item before 'pos' (range from 0 to items - 1).      //
//                                                                           //
// A new space will be inserted at the position 'pos', that is, items lie    //
// after pos (including the item at pos) will be moved one space downwords.  //
// If 'insitem' is not NULL, its contents will be copied into the new        //
// inserted space. Return a pointer to the new inserted space.               //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void* tetgenmesh::list::insert(int pos, void* insitem)
{
  if (pos >= items) {
    return append(insitem);
  }
  // Do we have enough space.
  if (items == maxitems) {
    char* newbase = (char *) realloc(base, (maxitems + expandsize) *
                                     itembytes);
    if (newbase == (char *) NULL) {
      terminatetetgen(1);
    }
    base = newbase;
    maxitems += expandsize;
  }
  // Do block move.
  memmove(base + (pos + 1) * itembytes,   // dest
          base + pos * itembytes,         // src
          (items - pos) * itembytes);     // size in bytes
  // Insert the item.
  if (insitem != (void *) NULL) {
    memcpy(base + pos * itembytes, insitem, itembytes);
  }
  items++;
  return (void *) (base + pos * itembytes);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// del()    Delete an item at 'pos' (range from 0 to items - 1).             //
//                                                                           //
// The space at 'pos' will be overlapped by other item. If 'order' is 1, the //
// remaining items of the list have the same order as usual, i.e., items lie //
// after pos will be moved one space upwords. If 'order' is 0, the last item //
// of the list will be moved up to pos.                                      //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::list::del(int pos, int order)
{
  // If 'pos' is the last item of the list, nothing need to do.
  if (pos >= 0 && pos < items - 1) {
    if (order == 1) {
      // Do block move. 
      memmove(base + pos * itembytes,       // dest
              base + (pos + 1) * itembytes, // src
              (items - pos - 1) * itembytes);
    } else {
      // Use the last item to overlap the del item.
      memcpy(base + pos * itembytes, // item at pos
             base + (items - 1) * itembytes, // item at last
             itembytes);
    }
  }
  if (items > 0) {
    items--;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// hasitem()    Search in this list to find if 'checkitem' exists.           //
//                                                                           //
// This routine assumes that a linear order function has been set.  It loops //
// through the entire list, compares each item to 'checkitem'. If it exists, //
// return its position (between 0 to items - 1), otherwise, return -1.       //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::list::hasitem(void* checkitem)
{
  int i;

  for (i = 0; i < items; i++) {
    if (comp != (compfunc) NULL) {
      if ((* comp)((void *)(base + i * itembytes), checkitem) == 0) {
        return i;
      }
    }
  }
  return -1;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// memorypool()   The constructors of memorypool.                            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

tetgenmesh::memorypool::memorypool()
{
  firstblock = nowblock = (void **) NULL;
  nextitem = (void *) NULL;
  deaditemstack = (void *) NULL;
  pathblock = (void **) NULL;
  pathitem = (void *) NULL;
  itemwordtype = POINTER;
  alignbytes = 0;
  itembytes = itemwords = 0;
  itemsperblock = 0;
  items = maxitems = 0l;
  unallocateditems = 0;
  pathitemsleft = 0;
}

tetgenmesh::memorypool::
memorypool(int bytecount, int itemcount, enum wordtype wtype, int alignment)
{
  poolinit(bytecount, itemcount, wtype, alignment);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// ~memorypool()   Free to the operating system all memory taken by a pool.  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

tetgenmesh::memorypool::~memorypool()
{
  while (firstblock != (void **) NULL) {
    nowblock = (void **) *(firstblock);
    free(firstblock);
    firstblock = nowblock;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// poolinit()    Initialize a pool of memory for allocation of items.        //
//                                                                           //
// A `pool' is created whose records have size at least `bytecount'.  Items  //
// will be allocated in `itemcount'-item blocks.  Each item is assumed to be //
// a collection of words, and either pointers or floating-point values are   //
// assumed to be the "primary" word type.  (The "primary" word type is used  //
// to determine alignment of items.)  If `alignment' isn't zero, all items   //
// will be `alignment'-byte aligned in memory.  `alignment' must be either a //
// multiple or a factor of the primary word size;  powers of two are safe.   //
// `alignment' is normally used to create a few unused bits at the bottom of //
// each item's pointer, in which information may be stored.                  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::memorypool::
poolinit(int bytecount, int itemcount, enum wordtype wtype, int alignment)
{
  int wordsize;

  // Initialize values in the pool.
  itemwordtype = wtype;
  wordsize = (itemwordtype == POINTER) ? sizeof(void *) : sizeof(REAL);
  // Find the proper alignment, which must be at least as large as:
  //   - The parameter `alignment'.
  //   - The primary word type, to avoid unaligned accesses.
  //   - sizeof(void *), so the stack of dead items can be maintained
  //       without unaligned accesses.
  if (alignment > wordsize) {
    alignbytes = alignment;
  } else {
    alignbytes = wordsize;
  }
  if ((int) sizeof(void *) > alignbytes) {
    alignbytes = (int) sizeof(void *);
  }
  itemwords = ((bytecount + alignbytes - 1) /  alignbytes)
            * (alignbytes / wordsize);
  itembytes = itemwords * wordsize;
  itemsperblock = itemcount;

  // Allocate a block of items.  Space for `itemsperblock' items and one
  //   pointer (to point to the next block) are allocated, as well as space
  //   to ensure alignment of the items. 
  firstblock = (void **) malloc(itemsperblock * itembytes + sizeof(void *)
                                + alignbytes); 
  if (firstblock == (void **) NULL) {
    terminatetetgen(1);
  }
  // Set the next block pointer to NULL.
  *(firstblock) = (void *) NULL;
  restart();
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// restart()   Deallocate all items in this pool.                            //
//                                                                           //
// The pool is returned to its starting state, except that no memory is      //
// freed to the operating system.  Rather, the previously allocated blocks   //
// are ready to be reused.                                                   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::memorypool::restart()
{
  // unsigned long alignptr;
  uintptr_t alignptr;

  items = 0;
  maxitems = 0;

  // Set the currently active block.
  nowblock = firstblock;
  // Find the first item in the pool.  Increment by the size of (void *).
  // alignptr = (unsigned long) (nowblock + 1);
  alignptr = (uintptr_t) (nowblock + 1);
  // Align the item on an `alignbytes'-byte boundary.
  // nextitem = (void *)
  //   (alignptr + (unsigned long) alignbytes -
  //    (alignptr % (unsigned long) alignbytes));
  nextitem = (void *)
    (alignptr + (uintptr_t) alignbytes -
     (alignptr % (uintptr_t) alignbytes));
  // There are lots of unallocated items left in this block.
  unallocateditems = itemsperblock;
  // The stack of deallocated items is empty.
  deaditemstack = (void *) NULL;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// alloc()   Allocate space for an item.                                     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void* tetgenmesh::memorypool::alloc()
{
  void *newitem;
  void **newblock;
  // unsigned long alignptr;
  uintptr_t alignptr;

  // First check the linked list of dead items.  If the list is not 
  //   empty, allocate an item from the list rather than a fresh one.
  if (deaditemstack != (void *) NULL) {
    newitem = deaditemstack;                     // Take first item in list.
    deaditemstack = * (void **) deaditemstack;
  } else {
    // Check if there are any free items left in the current block.
    if (unallocateditems == 0) {
      // Check if another block must be allocated.
      if (*nowblock == (void *) NULL) {
        // Allocate a new block of items, pointed to by the previous block.
        //marktag
		  //newblock = (void **) malloc(itemsperblock * itembytes + sizeof(void *) 
                                   // + alignbytes);
        if (newblock == (void **) NULL) {
          return;
			//terminatetetgen(1);
        }
        *nowblock = (void *) newblock;
        // The next block pointer is NULL.
        *newblock = (void *) NULL;
      }
      // Move to the new block.
      nowblock = (void **) *nowblock;
      // Find the first item in the block.
      //   Increment by the size of (void *).
      // alignptr = (unsigned long) (nowblock + 1);
      alignptr = (uintptr_t) (nowblock + 1);
      // Align the item on an `alignbytes'-byte boundary.
      // nextitem = (void *)
      //   (alignptr + (unsigned long) alignbytes -
      //    (alignptr % (unsigned long) alignbytes));
      nextitem = (void *)
        (alignptr + (uintptr_t) alignbytes -
         (alignptr % (uintptr_t) alignbytes));
      // There are lots of unallocated items left in this block.
      unallocateditems = itemsperblock;
    }
    // Allocate a new item.
    newitem = nextitem;
    // Advance `nextitem' pointer to next free item in block.
    if (itemwordtype == POINTER) {
      nextitem = (void *) ((void **) nextitem + itemwords);
    } else {
      nextitem = (void *) ((REAL *) nextitem + itemwords);
    }
    unallocateditems--;
    maxitems++;
  }
  items++;
  return newitem;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// dealloc()   Deallocate space for an item.                                 //
//                                                                           //
// The deallocated space is stored in a queue for later reuse.               //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::memorypool::dealloc(void *dyingitem)
{
  // Push freshly killed item onto stack.
  *((void **) dyingitem) = deaditemstack;
  deaditemstack = dyingitem;
  items--;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// traversalinit()   Prepare to traverse the entire list of items.           //
//                                                                           //
// This routine is used in conjunction with traverse().                      //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::memorypool::traversalinit()
{
  // unsigned long alignptr;
  uintptr_t alignptr;

  // Begin the traversal in the first block.
  pathblock = firstblock;
  // Find the first item in the block.  Increment by the size of (void *).
  // alignptr = (unsigned long) (pathblock + 1);
  alignptr = (uintptr_t) (pathblock + 1);
  // Align with item on an `alignbytes'-byte boundary.
  // pathitem = (void *)
  //   (alignptr + (unsigned long) alignbytes -
  //    (alignptr % (unsigned long) alignbytes));
  pathitem = (void *)
    (alignptr + (uintptr_t) alignbytes -
     (alignptr % (uintptr_t) alignbytes));
  // Set the number of items left in the current block.
  pathitemsleft = itemsperblock;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// traverse()   Find the next item in the list.                              //
//                                                                           //
// This routine is used in conjunction with traversalinit().  Be forewarned  //
// that this routine successively returns all items in the list, including   //
// deallocated ones on the deaditemqueue. It's up to you to figure out which //
// ones are actually dead.  It can usually be done more space-efficiently by //
// a routine that knows something about the structure of the item.           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void* tetgenmesh::memorypool::traverse()
{
  void *newitem;
  // unsigned long alignptr;
  uintptr_t alignptr;

  // Stop upon exhausting the list of items.
  if (pathitem == nextitem) {
    return (void *) NULL;
  }
  // Check whether any untraversed items remain in the current block.
  if (pathitemsleft == 0) {
    // Find the next block.
    pathblock = (void **) *pathblock;
    // Find the first item in the block.  Increment by the size of (void *).
    // alignptr = (unsigned long) (pathblock + 1);
    alignptr = (uintptr_t) (pathblock + 1);
    // Align with item on an `alignbytes'-byte boundary.
    // pathitem = (void *)
    //   (alignptr + (unsigned long) alignbytes -
    //    (alignptr % (unsigned long) alignbytes));
    pathitem = (void *)
      (alignptr + (uintptr_t) alignbytes -
       (alignptr % (uintptr_t) alignbytes));
    // Set the number of items left in the current block.
    pathitemsleft = itemsperblock;
  }
  newitem = pathitem;
  // Find the next item in the block.
  if (itemwordtype == POINTER) {
    pathitem = (void *) ((void **) pathitem + itemwords);
  } else {
    pathitem = (void *) ((REAL *) pathitem + itemwords);
  }
  pathitemsleft--;
  return newitem;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// makepoint2tetmap()    Construct a mapping from points to tetrahedra.      //
//                                                                           //
// Traverses all the tetrahedra,  provides each corner of each tetrahedron   //
// with a pointer to that tetrahedera.  Some pointers will be overwritten by //
// other pointers because each point may be a corner of several tetrahedra,  //
// but in the end every point will point to a tetrahedron that contains it.  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::makepoint2tetmap()
{
  triface tetloop;
  point pointptr;

  if (b->verbose > 2) {
    //printf("  Constructing mapping from points to tetrahedra.\n");
  }

  // Initialize the point2tet field of each point.
  points->traversalinit();
  pointptr = pointtraverse();
  while (pointptr != (point) NULL) {
    setpoint2tet(pointptr, (tetrahedron) NULL);
    pointptr = pointtraverse();
  }

  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  while (tetloop.tet != (tetrahedron *) NULL) {
    // Check all four points of the tetrahedron.
    tetloop.loc = 0;
    pointptr = org(tetloop);
    setpoint2tet(pointptr, encode(tetloop));
    pointptr = dest(tetloop);
    setpoint2tet(pointptr, encode(tetloop));
    pointptr = apex(tetloop);
    setpoint2tet(pointptr, encode(tetloop));
    pointptr = oppo(tetloop);
    setpoint2tet(pointptr, encode(tetloop));
    // Get the next tetrahedron in the list.
    tetloop.tet = tetrahedrontraverse();
  }
}

void tetgenmesh::makepoint2segmap()
{
  face segloop;
  point *ppt;

  if (b->verbose > 2) {
    //printf("  Constructing mapping from points to segments.\n");
  }

  segloop.shver = 0;
  subsegs->traversalinit();
  segloop.sh = shellfacetraverse(subsegs);
  while (segloop.sh != NULL) {
    ppt = (point *) &(segloop.sh[3]);
    setpoint2seg(ppt[0], sencode(segloop));
    setpoint2seg(ppt[1], sencode(segloop));
    segloop.sh = shellfacetraverse(subsegs);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// makeindex2pointmap()    Create a map from index to vertices.              //
//                                                                           //
// 'idx2verlist' returns the created map.  Traverse all vertices, a pointer  //
// to each vertex is set into the array.  The pointer to the first vertex is //
// saved in 'idx2verlist[0]'.  Don't forget to minus 'in->firstnumber' when  //
// to get the vertex form its index.                                         //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::makeindex2pointmap(point*& idx2verlist)
{
  point pointloop;
  int idx;

  if (b->verbose > 1) {
    //printf("  Constructing mapping from indices to points.\n");
  }

  idx2verlist = new point[points->items];

  points->traversalinit();
  pointloop = pointtraverse();
  idx = 0;
  while (pointloop != (point) NULL) {
    idx2verlist[idx] = pointloop;
    idx++;
    pointloop = pointtraverse();
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// makesegmentmap(), makesubfacemap(), maketetrahedronmap()                  //
//                                                                           //
// Create a map from vertex indices to segments, subfaces, and tetrahedra    //
// sharing at the same vertices.                                             //
//                                                                           //
// The map is stored in two arrays: 'idx2___list' and '___sperverlist', they //
// form a sparse matrix whose size is (n+1)x(n+1), where n is the number of  //
// segments, subfaces, or tetrahedra. 'idx2___list' contains row information //
// and '___sperverlist' contains all non-zero elements.  The i-th entry of   //
// 'idx2___list' is the starting position of i-th row's non-zero elements in //
// '___sperverlist'.  The number of elements of i-th row is (i+1)-th entry   //
// minus i-th entry of 'idx2___list'.                                        //
//                                                                           //
// NOTE: These two arrays will be created inside this routine, don't forget  //
// to free them after using.                                                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::makesegmentmap(int*& idx2seglist, shellface**& segsperverlist)
{
  shellface *shloop;
  int i, j, k;

  if (b->verbose > 1) {
    //printf("  Constructing mapping from points to segments.\n");
  }

  // Create and initialize 'idx2seglist'.
  idx2seglist = new int[points->items + 1];
  for (i = 0; i < points->items + 1; i++) idx2seglist[i] = 0;

  // Loop the set of segments once, counter the number of segments sharing
  //   each vertex.
  subsegs->traversalinit();
  shloop = shellfacetraverse(subsegs);
  while (shloop != (shellface *) NULL) {
    // Increment the number of sharing segments for each endpoint.
    for (i = 0; i < 2; i++) {
      j = pointmark((point) shloop[3 + i]) - in->firstnumber;
      idx2seglist[j]++;
    }
    shloop = shellfacetraverse(subsegs);
  }

  // Calculate the total length of array 'facesperverlist'.
  j = idx2seglist[0];
  idx2seglist[0] = 0;  // Array starts from 0 element.
  for (i = 0; i < points->items; i++) {
    k = idx2seglist[i + 1];
    idx2seglist[i + 1] = idx2seglist[i] + j;
    j = k;
  }
  // The total length is in the last unit of idx2seglist.
  segsperverlist = new shellface*[idx2seglist[i]];
  // Loop the set of segments again, set the info. of segments per vertex.
  subsegs->traversalinit();
  shloop = shellfacetraverse(subsegs);
  while (shloop != (shellface *) NULL) {
    for (i = 0; i < 2; i++) {
      j = pointmark((point) shloop[3 + i]) - in->firstnumber;
      segsperverlist[idx2seglist[j]] = shloop;
      idx2seglist[j]++;
    }
    shloop = shellfacetraverse(subsegs);
  }
  // Contents in 'idx2seglist' are shifted, now shift them back.
  for (i = points->items - 1; i >= 0; i--) {
    idx2seglist[i + 1] = idx2seglist[i];
  }
  idx2seglist[0] = 0;
}

void tetgenmesh::makesubfacemap(int*& idx2facelist,
  shellface**& facesperverlist)
{
  shellface *shloop;
  int i, j, k;

  if (b->verbose > 1) {
    //printf("  Constructing mapping from points to subfaces.\n");
  }

  // Create and initialize 'idx2facelist'.
  idx2facelist = new int[points->items + 1];
  for (i = 0; i < points->items + 1; i++) idx2facelist[i] = 0;

  // Loop the set of subfaces once, counter the number of subfaces sharing
  //   each vertex.
  subfaces->traversalinit();
  shloop = shellfacetraverse(subfaces);
  while (shloop != (shellface *) NULL) {
    // Increment the number of sharing segments for each endpoint.
    for (i = 0; i < 3; i++) {
      j = pointmark((point) shloop[3 + i]) - in->firstnumber;
      idx2facelist[j]++;
    }
    shloop = shellfacetraverse(subfaces);
  }

  // Calculate the total length of array 'facesperverlist'.
  j = idx2facelist[0];
  idx2facelist[0] = 0;  // Array starts from 0 element.
  for (i = 0; i < points->items; i++) {
    k = idx2facelist[i + 1];
    idx2facelist[i + 1] = idx2facelist[i] + j;
    j = k;
  }
  // The total length is in the last unit of idx2facelist.
  facesperverlist = new shellface*[idx2facelist[i]];
  // Loop the set of segments again, set the info. of segments per vertex.
  subfaces->traversalinit();
  shloop = shellfacetraverse(subfaces);
  while (shloop != (shellface *) NULL) {
    for (i = 0; i < 3; i++) {
      j = pointmark((point) shloop[3 + i]) - in->firstnumber;
      facesperverlist[idx2facelist[j]] = shloop;
      idx2facelist[j]++;
    }
    shloop = shellfacetraverse(subfaces);
  }
  // Contents in 'idx2facelist' are shifted, now shift them back.
  for (i = points->items - 1; i >= 0; i--) {
    idx2facelist[i + 1] = idx2facelist[i];
  }
  idx2facelist[0] = 0;
}

void tetgenmesh::maketetrahedronmap(int*& idx2tetlist, 
  tetrahedron**& tetsperverlist)
{
  tetrahedron *tetloop;
  int i, j, k;

  if (b->verbose > 1) {
    //printf("  Constructing mapping from points to tetrahedra.\n");
  }

  // Create and initialize 'idx2tetlist'.
  idx2tetlist = new int[points->items + 1];
  for (i = 0; i < points->items + 1; i++) idx2tetlist[i] = 0;

  // Loop the set of tetrahedra once, counter the number of tetrahedra
  //   sharing each vertex.
  tetrahedrons->traversalinit();
  tetloop = tetrahedrontraverse();
  while (tetloop != (tetrahedron *) NULL) {
    // Increment the number of sharing tetrahedra for each endpoint.
    for (i = 0; i < 4; i++) {
      j = pointmark((point) tetloop[4 + i]) - in->firstnumber;
      idx2tetlist[j]++;
    }
    tetloop = tetrahedrontraverse();
  }

  // Calculate the total length of array 'tetsperverlist'.
  j = idx2tetlist[0];
  idx2tetlist[0] = 0;  // Array starts from 0 element.
  for (i = 0; i < points->items; i++) {
    k = idx2tetlist[i + 1];
    idx2tetlist[i + 1] = idx2tetlist[i] + j;
    j = k;
  }
  // The total length is in the last unit of idx2tetlist.
  tetsperverlist = new tetrahedron*[idx2tetlist[i]];
  // Loop the set of tetrahedra again, set the info. of tet. per vertex.
  tetrahedrons->traversalinit();
  tetloop = tetrahedrontraverse();
  while (tetloop != (tetrahedron *) NULL) {
    for (i = 0; i < 4; i++) {
      j = pointmark((point) tetloop[4 + i]) - in->firstnumber;
      tetsperverlist[idx2tetlist[j]] = tetloop;
      idx2tetlist[j]++;
    }
    tetloop = tetrahedrontraverse();
  }
  // Contents in 'idx2tetlist' are shifted, now shift them back.
  for (i = points->items - 1; i >= 0; i--) {
    idx2tetlist[i + 1] = idx2tetlist[i];
  }
  idx2tetlist[0] = 0;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// dummyinit()    Initialize the tetrahedron that fills "outer space" and    //
//                the omnipresent subface.                                   //
//                                                                           //
// The tetrahedron that fills "outer space" called 'dummytet', is pointed to //
// by every tetrahedron and subface on a boundary (be it outer or inner) of  //
// the tetrahedralization. Also, 'dummytet' points to one of the tetrahedron //
// on the convex hull(until the holes and concavities are carved), making it //
// possible to find a starting tetrahedron for point location.               //
//                                                                           //
// The omnipresent subface,'dummysh', is pointed to by every tetrahedron or  //
// subface that doesn't have a full complement of real subface to point to.  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

__device__ void tetgenmesh::dummyinit(int tetwords, int shwords)
{
  unsigned long alignptr;

  // Set up 'dummytet', the 'tetrahedron' that occupies "outer space".
  //marktag
  //dummytetbase = (tetrahedron *) new char[tetwords * sizeof(tetrahedron)
                                         // + tetrahedrons->alignbytes];
  // Align 'dummytet' on a 'tetrahedrons->alignbytes'-byte boundary.
  alignptr = (unsigned long) dummytetbase;
  dummytet = (tetrahedron *)
    (alignptr + (unsigned long) tetrahedrons->alignbytes
     - (alignptr % (unsigned long) tetrahedrons->alignbytes));
  // Initialize the four adjoining tetrahedra to be "outer space". These
  //   will eventually be changed by various bonding operations, but their
  //   values don't really matter, as long as they can legally be
  //   dereferenced.
  dummytet[0] = (tetrahedron) dummytet;
  dummytet[1] = (tetrahedron) dummytet;
  dummytet[2] = (tetrahedron) dummytet;
  dummytet[3] = (tetrahedron) dummytet;
  // Four null vertex points.
  dummytet[4] = (tetrahedron) NULL;
  dummytet[5] = (tetrahedron) NULL;
  dummytet[6] = (tetrahedron) NULL;
  dummytet[7] = (tetrahedron) NULL;

  //if (b->useshelles) {
  //  // Set up 'dummysh', the omnipresent "subface" pointed to by any
  //  //   tetrahedron side or subface end that isn't attached to a real
  //  //   subface.
  //  dummyshbase = (shellface *) new char[shwords * sizeof(shellface)
  //                                       + subfaces->alignbytes];
  //  // Align 'dummysh' on a 'subfaces->alignbytes'-byte boundary.
  //  alignptr = (unsigned long) dummyshbase;
  //  dummysh = (shellface *)
  //    (alignptr + (unsigned long) subfaces->alignbytes
  //     - (alignptr % (unsigned long) subfaces->alignbytes));
  //  // Initialize the three adjoining subfaces to be the omnipresent
  //  //   subface. These will eventually be changed by various bonding
  //  //   operations, but their values don't really matter, as long as they
  //  //   can legally be dereferenced.
  //  dummysh[0] = (shellface) dummysh;
  //  dummysh[1] = (shellface) dummysh;
  //  dummysh[2] = (shellface) dummysh;
  //  // Three null vertex points.
  //  dummysh[3] = (shellface) NULL;
  //  dummysh[4] = (shellface) NULL;
  //  dummysh[5] = (shellface) NULL;
  //  // Initialize the two adjoining tetrahedra to be "outer space".
  //  dummysh[6] = (shellface) dummytet;
  //  dummysh[7] = (shellface) dummytet;
  //  // Initialize the three adjoining subsegments to be "out boundary".
  //  dummysh[8]  = (shellface) dummysh;
  //  dummysh[9]  = (shellface) dummysh;
  //  dummysh[10] = (shellface) dummysh;
  //  // Initialize the pointer to badface structure.
  //  dummysh[11] = (shellface) NULL;
  //  // Initialize the four adjoining subfaces of 'dummytet' to be the
  //  //   omnipresent subface.
  //  dummytet[8 ] = NULL;
  //  dummytet[9 ] = NULL;
  //}
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// initializepools()    Calculate the sizes of the point, tetrahedron, and   //
//                      subface. Initialize their memory pools.              //
//                                                                           //
// This routine also computes the indices 'pointmarkindex', 'point2simindex',//
// and 'point2pbcptindex' used to find values within each point;  computes   //
// indices 'highorderindex', 'elemattribindex', and 'volumeboundindex' used  //
// to find values within each tetrahedron.                                   //
//                                                                           //
// There are two types of boundary elements, which are subfaces and subsegs, //
// they are stored in seperate pools. However, the data structures of them   //
// are the same.  A subsegment can be regarded as a degenerate subface, i.e.,//
// one of its three corners is not used. We set the apex of it be 'NULL' to  //
// distinguish it's a subsegment.                                            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::initializepools()
{
  enum wordtype wtype;
  int pointsize, elesize, shsize;

  // Default checkpbc = 0;
  if ((b->plc || b->refine) && (in->pbcgrouplist != NULL)) {
    checkpbcs = 1;
  }
  // Default varconstraint = 0;
  if (in->segmentconstraintlist || in->facetconstraintlist) {
    varconstraint = 1;
  }

  // The index within each point at which its metric tensor is found. It is
  //   saved directly after the list of point attributes.
  pointmtrindex = 3 + in->numberofpointattributes;
  // Decide the size (1, 3, or 6) of the metric tensor.
  if (b->metric) {
    // For '-m' option. A tensor field is provided (*.mtr or *.b.mtr file).
    if (bgm != (tetgenmesh *) NULL) {
      // A background mesh is allocated. It may not exist though.
      sizeoftensor = (bgm->in != (tetgenio *) NULL) ? 
        bgm->in->numberofpointmtrs : in->numberofpointmtrs;
    } else {
      // No given background mesh - Itself is a background mesh.
      sizeoftensor = in->numberofpointmtrs;
    }
    // Make sure sizeoftensor is at least 1.
    sizeoftensor = (sizeoftensor > 0) ? sizeoftensor : 1;
  } else {
    // For '-q' option. Make sure to have space for saving a scalar value.
    sizeoftensor = b->quality ? 1 : 0;
  }
  // The index within each point at which an element pointer is found, where
  //   the index is measured in pointers. Ensure the index is aligned to a
  //   sizeof(tetrahedron)-byte address.
  point2simindex = ((pointmtrindex + sizeoftensor) * sizeof(REAL)
                 + sizeof(tetrahedron) - 1) / sizeof(tetrahedron);
  if (b->plc || b->refine || b->voroout) {
    // Increase the point size by four pointers, which are:
    //   - a pointer to a tet, read by point2tet();
    //   - a pointer to a subface, read by point2sh();
    //   - a pointer to a subsegment, read by point2seg();
    //   - a pointer to a parent point, read by point2ppt()).
    if (b->metric) {
      // Increase one pointer to a tet of the background mesh.
      pointsize = (point2simindex + 5) * sizeof(tetrahedron);
    } else {
      pointsize = (point2simindex + 4) * sizeof(tetrahedron);
    }
    // The index within each point at which a pbc point is found.
    point2pbcptindex = (pointsize + sizeof(tetrahedron) - 1)
                     / sizeof(tetrahedron);
    if (checkpbcs) {
      // Increase the size by one pointer to a corresponding pbc point,
      //   read by point2pbcpt().
      pointsize = (point2pbcptindex + 1) * sizeof(tetrahedron);
    }
  } else {
    // Increase the point size by FOUR pointer, which are:
    //   - a pointer to a tet, read by point2tet();
    //   - a pointer to a subface, read by point2sh(); -- !! Unused !!
    //   - a pointer to a subsegment, read by point2seg(); -- !! Unused !!
    //   - a pointer to a parent point, read by point2ppt()). -- Used by btree.
    pointsize = (point2simindex + 4) * sizeof(tetrahedron);
  }
  // The index within each point at which the boundary marker is found,
  //   Ensure the point marker is aligned to a sizeof(int)-byte address.
  pointmarkindex = (pointsize + sizeof(int) - 1) / sizeof(int);
  // Now point size is the ints (inidcated by pointmarkindex) plus:
  //   - an integer for boundary marker;
  //   - an integer for vertex type;
  //pointsize = (pointmarkindex + 2) * sizeof(int); // Wrong for 64 bit.
  pointsize = (pointmarkindex + 2) * sizeof(tetrahedron);
  // Decide the wordtype used in vertex pool.
  wtype = (sizeof(REAL) >= sizeof(tetrahedron)) ? FLOATINGPOINT : POINTER;
  // Initialize the pool of vertices.
  //marktag
  //points = new memorypool(pointsize, VERPERBLOCK, wtype, 0);

  //if (b->useshelles) {
  //  dummypoint = (point) new char[pointsize]; // For abovepoint.
  //}

  // The number of bytes occupied by a tetrahedron.  There are four pointers
  //   to other tetrahedra, four pointers to corners, and possibly four
  //   pointers to subfaces (or six pointers to subsegments (used in
  //   segment recovery only)).
  elesize = (8 + b->useshelles * 2) * sizeof(tetrahedron);
  // If Voronoi diagram is wanted, make sure we have additional space.
  if (b->voroout) {
    elesize = (8 + 4) * sizeof(tetrahedron);
  }
  // The index within each element at which its attributes are found, where
  //   the index is measured in REALs. 
  elemattribindex = (elesize + sizeof(REAL) - 1) / sizeof(REAL);
  // The index within each element at which the maximum voulme bound is
  //   found, where the index is measured in REALs.  Note that if the
  //   `b->regionattrib' flag is set, an additional attribute will be added.
  volumeboundindex = elemattribindex + in->numberoftetrahedronattributes
                   + (b->regionattrib > 0);
  // If element attributes or an constraint are needed, increase the number
  //   of bytes occupied by an element.
  if (b->varvolume) {
    elesize = (volumeboundindex + 1) * sizeof(REAL);
  } else if (in->numberoftetrahedronattributes + b->regionattrib > 0) {
    elesize = volumeboundindex * sizeof(REAL);
  }
  // If element neighbor graph is requested (-n switch), an additional
  //   integer is allocated for each element.
  // elemmarkerindex = (elesize + sizeof(int) - 1) / sizeof(int);
  elemmarkerindex = (elesize + sizeof(int) - 1) / sizeof(int);
  // if (b->neighout || b->voroout) {
    // elesize = (elemmarkerindex + 1) * sizeof(int);
    // Allocate one slot for the element marker. The actual need isa size
    //   of an integer. We allocate enough space (a pointer) for alignment
    //   for 64 bit system. Thanks Liu Yang (LORIA/INRIA) for reporting
    //   this problem. 
    elesize = elesize + sizeof(tetrahedron);
  // }
  // If -o2 switch is used, an additional pointer pointed to the list of
  //   higher order nodes is allocated for each element.
  highorderindex = (elesize + sizeof(tetrahedron) - 1) / sizeof(tetrahedron);
  if (b->order == 2) {
    elesize = (highorderindex + 1) * sizeof(tetrahedron);
  }
  // Having determined the memory size of an element, initialize the pool.
  //marktag
  //tetrahedrons = new memorypool(elesize, ELEPERBLOCK, POINTER, 8);

  //if (b->useshelles) {
  //  // The number of bytes occupied by a subface.  The list of pointers
  //  //   stored in a subface are: three to other subfaces, three to corners,
  //  //   three to subsegments, two to tetrahedra, and one to a badface.
  //  shsize = 12 * sizeof(shellface);
  //  // The index within each subface at which the maximum area bound is
  //  //   found, where the index is measured in REALs.
  //  areaboundindex = (shsize + sizeof(REAL) - 1) / sizeof(REAL);
  //  // If -q switch is in use, increase the number of bytes occupied by
  //  //   a subface for saving maximum area bound.
  //  if (b->quality && varconstraint) {
  //    shsize = (areaboundindex + 1) * sizeof(REAL);
  //  } else {
  //    shsize = areaboundindex * sizeof(REAL);
  //  }
  //  // The index within subface at which the facet marker is found. Ensure
  //  //   the marker is aligned to a sizeof(int)-byte address.
  //  shmarkindex = (shsize + sizeof(int) - 1) / sizeof(int);
  //  // Increase the number of bytes by two or three integers, one for facet
  //  //   marker, one for shellface type, and optionally one for pbc group.
  //  shsize = (shmarkindex + 2 + checkpbcs) * sizeof(int);
  //  // Initialize the pool of subfaces. Each subface record is eight-byte
  //  //   aligned so it has room to store an edge version (from 0 to 5) in
  //  //   the least three bits.
  //  subfaces = new memorypool(shsize, SUBPERBLOCK, POINTER, 8);
  //  // Initialize the pool of subsegments. The subsegment's record is same
  //  //   with subface.
  //  subsegs = new memorypool(shsize, SUBPERBLOCK, POINTER, 8);
  //  // Initialize the pool for tet-subseg connections.
  //  tet2segpool = new memorypool(6*sizeof(shellface), SUBPERBLOCK, POINTER, 0);
  //  // Initialize the pool for tet-subface connections.
  //  tet2subpool = new memorypool(4*sizeof(shellface), SUBPERBLOCK, POINTER, 0);
  //  // Initialize arraypools for segment & facet recovery.
  //  subsegstack = new arraypool(sizeof(face), 10);
  //  subfacstack = new arraypool(sizeof(face), 10);
  //  // Initialize the "outer space" tetrahedron and omnipresent subface.
  //  dummyinit(tetrahedrons->itemwords, subfaces->itemwords);
  //} else {
    // Initialize the "outer space" tetrahedron.
    dummyinit(tetrahedrons->itemwords, 0);
  //}
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// tetrahedrondealloc()    Deallocate space for a tet., marking it dead.     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::tetrahedrondealloc(tetrahedron *dyingtetrahedron)
{
  // Set tetrahedron's vertices to NULL. This makes it possible to detect
  //   dead tetrahedra when traversing the list of all tetrahedra.
  dyingtetrahedron[4] = (tetrahedron) NULL;
  // dyingtetrahedron[5] = (tetrahedron) NULL;
  // dyingtetrahedron[6] = (tetrahedron) NULL;
  dyingtetrahedron[7] = (tetrahedron) NULL;

  if (b->useshelles) {
    // Dealloc the space to subfaces/subsegments.
    if (dyingtetrahedron[8] != NULL) {
      tet2segpool->dealloc((shellface *) dyingtetrahedron[8]);
    }
    if (dyingtetrahedron[9] != NULL) {
      tet2subpool->dealloc((shellface *) dyingtetrahedron[9]);
    }
  }

  tetrahedrons->dealloc((void *) dyingtetrahedron);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// tetrahedrontraverse()    Traverse the tetrahedra, skipping dead ones.     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

tetgenmesh::tetrahedron* tetgenmesh::tetrahedrontraverse()
{
  tetrahedron *newtetrahedron;

  do {
    newtetrahedron = (tetrahedron *) tetrahedrons->traverse();
    if (newtetrahedron == (tetrahedron *) NULL) {
      return (tetrahedron *) NULL;
    }
  } while (newtetrahedron[7] == (tetrahedron) NULL);      // Skip dead ones.
  return newtetrahedron;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// shellfacedealloc()    Deallocate space for a shellface, marking it dead.  //
//                       Used both for dealloc a subface and subsegment.     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::shellfacedealloc(memorypool *pool, shellface *dyingsh)
{
  // Set shellface's vertices to NULL. This makes it possible to detect dead
  //   shellfaces when traversing the list of all shellfaces.
  dyingsh[3] = (shellface) NULL;
  dyingsh[4] = (shellface) NULL;
  dyingsh[5] = (shellface) NULL;
  pool->dealloc((void *) dyingsh);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// shellfacetraverse()    Traverse the subfaces, skipping dead ones. Used    //
//                        for both subfaces and subsegments pool traverse.   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

tetgenmesh::shellface* tetgenmesh::shellfacetraverse(memorypool *pool)
{
  shellface *newshellface;

  do {
    newshellface = (shellface *) pool->traverse();
    if (newshellface == (shellface *) NULL) {
      return (shellface *) NULL;
    }
  } while (newshellface[3] == (shellface) NULL);          // Skip dead ones.
  return newshellface;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// badfacedealloc()    Deallocate space for a badface, marking it dead.      //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::badfacedealloc(memorypool *pool, badface *dying)
{
  // Set badface's forg to NULL. This makes it possible to detect dead
  //   ones when traversing the list of all items.
  dying->forg = (point) NULL;
  pool->dealloc((void *) dying);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// badfacetraverse()    Traverse the pools, skipping dead ones.              //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

tetgenmesh::badface* tetgenmesh::badfacetraverse(memorypool *pool)
{
  badface *newsh;

  do {
    newsh = (badface *) pool->traverse();
    if (newsh == (badface *) NULL) {
      return (badface *) NULL;
    }
  } while (newsh->forg == (point) NULL);               // Skip dead ones.
  return newsh;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// pointdealloc()    Deallocate space for a point, marking it dead.          //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::pointdealloc(point dyingpoint)
{
  // Mark the point as dead. This  makes it possible to detect dead points
  //   when traversing the list of all points.
  setpointtype(dyingpoint, DEADVERTEX);
  points->dealloc((void *) dyingpoint);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// pointtraverse()    Traverse the points, skipping dead ones.               //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

tetgenmesh::point tetgenmesh::pointtraverse()
{
  point newpoint;

  do {
    newpoint = (point) points->traverse();
    if (newpoint == (point) NULL) {
      return (point) NULL;
    }
  } while (pointtype(newpoint) == DEADVERTEX);            // Skip dead ones.
  return newpoint;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// maketetrahedron()    Create a new tetrahedron.                            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::maketetrahedron(triface *newtet)
{
  newtet->tet = (tetrahedron *) tetrahedrons->alloc();
  // Initialize the four adjoining tetrahedra to be "outer space".
  newtet->tet[0] = (tetrahedron) dummytet;
  newtet->tet[1] = (tetrahedron) dummytet;
  newtet->tet[2] = (tetrahedron) dummytet;
  newtet->tet[3] = (tetrahedron) dummytet;
  // Four NULL vertices.
  newtet->tet[4] = (tetrahedron) NULL;
  newtet->tet[5] = (tetrahedron) NULL;
  newtet->tet[6] = (tetrahedron) NULL;
  newtet->tet[7] = (tetrahedron) NULL;
  // Initialize the four adjoining subfaces to be the omnipresent subface.
  if (b->useshelles) {
    newtet->tet[8 ] = NULL; 
    newtet->tet[9 ] = NULL; 
  }
  for (int i = 0; i < in->numberoftetrahedronattributes; i++) {
    setelemattribute(newtet->tet, i, 0.0);
  }
  if (b->varvolume) {
    setvolumebound(newtet->tet, -1.0);
  }
  // Initialize the marker (for flags).
  setelemmarker(newtet->tet, 0);
  // Initialize the location and version to be Zero.
  newtet->loc = 0;
  newtet->ver = 0;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// makeshellface()    Create a new shellface with version zero. Used for     //
//                    both subfaces and seusegments.                         //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::makeshellface(memorypool *pool, face *newface)
{
  newface->sh = (shellface *) pool->alloc();
  //Initialize the three adjoining subfaces to be the omnipresent subface.
  newface->sh[0] = (shellface) dummysh;
  newface->sh[1] = (shellface) dummysh;
  newface->sh[2] = (shellface) dummysh;
  // Three NULL vertices.
  newface->sh[3] = (shellface) NULL;
  newface->sh[4] = (shellface) NULL;
  newface->sh[5] = (shellface) NULL;
  // Initialize the two adjoining tetrahedra to be "outer space".
  newface->sh[6] = (shellface) dummytet;
  newface->sh[7] = (shellface) dummytet;
  // Initialize the three adjoining subsegments to be the omnipresent
  //   subsegments.
  newface->sh [8] = (shellface) dummysh;
  newface->sh [9] = (shellface) dummysh;
  newface->sh[10] = (shellface) dummysh;
  // Initialize the pointer to badface structure.
  newface->sh[11] = (shellface) NULL;
  if (b->quality && varconstraint) {
    // Initialize the maximum area bound.
    setareabound(*newface, 0.0);
  }
  // Clear the infection and marktest bits.
  suninfect(*newface);
  sunmarktest(*newface);
  // Set the boundary marker to zero.
  setshellmark(*newface, 0);
  // Set the type.
  setshelltype(*newface, NSHARP);
  if (checkpbcs) {
    // Set the pbcgroup be ivalid.
    setshellpbcgroup(*newface, -1);
  }
  // Initialize the version to be Zero.
  newface->shver = 0;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// makepoint()    Create a new point.                                        //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::makepoint(point* pnewpoint)
{
  int ptmark, i;

  *pnewpoint = (point) points->alloc();
  // Initialize three coordinates.
  (*pnewpoint)[0] = 0.0;
  (*pnewpoint)[1] = 0.0;
  (*pnewpoint)[2] = 0.0;
  // Initialize the list of user-defined attributes.
  for (i = 0; i < in->numberofpointattributes; i++) {
    (*pnewpoint)[3 + i] = 0.0;
  }
  // Initialize the metric tensor.
  for (i = 0; i < sizeoftensor; i++) {
    (*pnewpoint)[pointmtrindex + i] = 0.0;
  }
  if (b->plc || b->refine) {
    // Initialize the point-to-simplex filed.
    setpoint2tet(*pnewpoint, NULL);
    setpoint2sh(*pnewpoint, NULL);
    setpoint2seg(*pnewpoint, NULL);
    setpoint2ppt(*pnewpoint, NULL);
    if (b->metric) {
      setpoint2bgmtet(*pnewpoint, NULL);
    }
    if (checkpbcs) {
      // Initialize the other pointer to its pbc point.
      setpoint2pbcpt(*pnewpoint, NULL);
    }
  }
  // Initialize the point marker (starting from in->firstnumber).
  ptmark = (int) points->items - (in->firstnumber == 1 ? 0 : 1);
  setpointmark(*pnewpoint, ptmark);
  // Initialize the point type.
  setpointtype(*pnewpoint, UNUSEDVERTEX);
  // Clear the point flags.
  puninfect(*pnewpoint);
  //punmarktest(*pnewpoint);
}

////                                                                       ////
////                                                                       ////
//// mempool_cxx //////////////////////////////////////////////////////////////

//// geom_cxx /////////////////////////////////////////////////////////////////
////                                                                       ////
////                                                                       ////

// PI is the ratio of a circle's circumference to its diameter.

REAL tetgenmesh::PI = 3.14159265358979323846264338327950288419716939937510582;

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// Triangle-triangle intersection test                                       //
//                                                                           //
// The triangle-triangle intersection test is implemented with exact arithm- //
// etic. It exactly tells whether or not two triangles in three dimensions   //
// intersect.  Before implementing this test myself,  I tried two C codes    //
// (implemented by Thomas Moeller and Philippe Guigue, respectively), which  //
// are all public available. However both of them failed frequently. Another //
// unconvenience is both codes only tell whether or not the two triangles    //
// intersect without distinguishing the cases whether they exactly intersect //
// in interior or they just share a vertex or share an edge. The two latter  //
// cases are acceptable and should return not intersection in TetGen.        //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

// All the following routines require the input objects are not degenerate.
//   i.e., a triangle must has three non-collinear corners; an edge must
//   has two identical endpoints.  Degenerate cases should have to detect
//   first and then handled as special cases.

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// edge_vert_col_inter()    Test whether an edge (ab) and a collinear vertex //
//                          (p) are intersecting or not.                     //
//                                                                           //
// Possible cases are p is coincident to a (p = a), or to b (p = b), or p is //
// inside ab (a < p < b), or outside ab (p < a or p > b). These cases can be //
// quickly determined by comparing the corresponding coords of a, b, and p   //
// (which are not all equal).                                                //
//                                                                           //
// The return value indicates one of the three cases: DISJOINT, SHAREVERTEX  //
// (p = a or p = b), and INTERSECT (a < p < b).                              //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

enum tetgenmesh::interresult tetgenmesh::edge_vert_col_inter(REAL* A, REAL* B,
  REAL* P)
{
  int i = 0;
  do {
    if (A[i] < B[i]) {
      if (P[i] < A[i]) {
        return DISJOINT;
      } else if (P[i] > A[i]) {
        if (P[i] < B[i]) {
          return INTERSECT;
        } else if (P[i] > B[i]) {
          return DISJOINT;
        } else {
          // //assert(P[i] == B[i]);
          return SHAREVERTEX;
        }
      } else {
        // //assert(P[i] == A[i]);
        return SHAREVERTEX;
      }
    } else if (A[i] > B[i]) {
      if (P[i] < B[i]) {
        return DISJOINT;
      } else if (P[i] > B[i]) {
        if (P[i] < A[i]) {
          return INTERSECT;
        } else if (P[i] > A[i]) {
          return DISJOINT;
        } else {
          // //assert(P[i] == A[i]);
          return SHAREVERTEX;
        }
      } else {
        // //assert(P[i] == B[i]);
        return SHAREVERTEX;
      }
    }
    // i-th coordinates are equal, try i+1-th;
    i++;
  } while (i < 3);
  // Should never be here.
  return DISJOINT;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// edge_edge_cop_inter()    Test whether two coplanar edges (ab, and pq) are //
//                          intersecting or not.                             //
//                                                                           //
// Possible cases are ab and pq are disjointed, or proper intersecting (int- //
// ersect at a point other than their endpoints), or both collinear and int- //
// ersecting, or sharing at a common endpoint, or are coincident.            //
//                                                                           //
// A reference point R is required, which is exactly not coplanar with these //
// two edges.  Since the caller knows these two edges are coplanar, it must  //
// be able to provide (or calculate) such a point.                           //
//                                                                           //
// The return value indicates one of the four cases: DISJOINT, SHAREVERTEX,  //
// SHAREEDGE, and INTERSECT.                                                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

enum tetgenmesh::interresult tetgenmesh:: edge_edge_cop_inter(REAL* A, REAL* B,
  REAL* P, REAL* Q, REAL* R)
{
  REAL s1, s2, s3, s4;

#ifdef SELF_CHECK
  //assert(R != NULL);
#endif
  s1 = orient3d(A, B, R, P);
  s2 = orient3d(A, B, R, Q);
  if (s1 * s2 > 0.0) {
    // Both p and q are at the same side of ab.
    return DISJOINT;
  }
  s3 = orient3d(P, Q, R, A);
  s4 = orient3d(P, Q, R, B);
  if (s3 * s4 > 0.0) {
    // Both a and b are at the same side of pq.
    return DISJOINT;
  }

  // Possible degenerate cases are:
  //   (1) Only one of p and q is collinear with ab;
  //   (2) Both p and q are collinear with ab;
  //   (3) Only one of a and b is collinear with pq. 
  enum interresult abp, abq;
  enum interresult pqa, pqb;

  if (s1 == 0.0) {
    // p is collinear with ab.
    abp = edge_vert_col_inter(A, B, P);
    if (abp == INTERSECT) {
      // p is inside ab.
      return INTERSECT;
    }
    if (s2 == 0.0) {
      // q is collinear with ab. Case (2).
      abq = edge_vert_col_inter(A, B, Q);
      if (abq == INTERSECT) {
        // q is inside ab.
        return INTERSECT;
      }
      if (abp == SHAREVERTEX && abq == SHAREVERTEX) {
        // ab and pq are identical.
        return SHAREEDGE;
      }
      pqa = edge_vert_col_inter(P, Q, A);
      if (pqa == INTERSECT) {
        // a is inside pq.
        return INTERSECT;
      }
      pqb = edge_vert_col_inter(P, Q, B);
      if (pqb == INTERSECT) {
        // b is inside pq.
        return INTERSECT;
      }
      if (abp == SHAREVERTEX || abq == SHAREVERTEX) {
        // either p or q is coincident with a or b.
#ifdef SELF_CHECK
        // ONLY one case is possible, otherwise, shoule be SHAREEDGE.
        //assert(abp ^ abq);
#endif
        return SHAREVERTEX;
      }
      // The last case. They are disjointed.
#ifdef SELF_CHECK
      //assert((abp == DISJOINT) && (abp == abq && abq == pqa && pqa == pqb));
#endif
      return DISJOINT;
    } else {
      // p is collinear with ab. Case (1).
#ifdef SELF_CHECK
      //assert(abp == SHAREVERTEX || abp == DISJOINT);
#endif
      return abp;
    }
  }
  // p is NOT collinear with ab.
  if (s2 == 0.0) {
    // q is collinear with ab. Case (1).
    abq = edge_vert_col_inter(A, B, Q);
#ifdef SELF_CHECK
    //assert(abq == SHAREVERTEX || abq == DISJOINT || abq == INTERSECT);
#endif
    return abq;
  }

  // We have found p and q are not collinear with ab. However, it is still
  //   possible that a or b is collinear with pq (ONLY one of a and b).
  if (s3 == 0.0) {
    // a is collinear with pq. Case (3).
#ifdef SELF_CHECK
    //assert(s4 != 0.0);
#endif
    pqa = edge_vert_col_inter(P, Q, A);
#ifdef SELF_CHECK
    // This case should have been detected in above.
    //assert(pqa != SHAREVERTEX);
    //assert(pqa == INTERSECT || pqa == DISJOINT);
#endif
    return pqa;
  }
  if (s4 == 0.0) {
    // b is collinear with pq. Case (3).
#ifdef SELF_CHECK
    //assert(s3 != 0.0);
#endif
    pqb = edge_vert_col_inter(P, Q, B);
#ifdef SELF_CHECK
    // This case should have been detected in above.
    //assert(pqb != SHAREVERTEX);
    //assert(pqb == INTERSECT || pqb == DISJOINT);
#endif
    return pqb;
  }

  // ab and pq are intersecting properly.
  return INTERSECT;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// Notations                                                                 //
//                                                                           //
// Let ABC be the plane passes through a, b, and c;  ABC+ be the halfspace   //
// including the set of all points x, such that orient3d(a, b, c, x) > 0;    //
// ABC- be the other halfspace, such that for each point x in ABC-,          //
// orient3d(a, b, c, x) < 0.  For the set of x which are on ABC, orient3d(a, //
// b, c, x) = 0.                                                             //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// tri_vert_copl_inter()    Test whether a triangle (abc) and a coplanar     //
//                          point (p) are intersecting or not.               //
//                                                                           //
// Possible cases are p is inside abc, or on an edge of, or coincident with  //
// a vertex of, or outside abc.                                              //
//                                                                           //
// A reference point R is required. R is exactly not coplanar with abc and p.//
// Since the caller knows they are coplanar, it must be able to provide (or  //
// calculate) such a point.                                                  //
//                                                                           //
// The return value indicates one of the four cases: DISJOINT, SHAREVERTEX,  //
// and INTERSECT.                                                            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

enum tetgenmesh::interresult tetgenmesh::tri_vert_cop_inter(REAL* A, REAL* B,
  REAL* C, REAL* P, REAL* R)
{
  REAL s1, s2, s3;
  int sign;

#ifdef SELF_CHECK
  //assert(R != (REAL *) NULL);
#endif
  // Adjust the orientation of a, b, c and r, so that we can assume that
  //   r is strictly in ABC- (i.e., r is above ABC wrt. right-hand rule).
  s1 = orient3d(A, B, C, R);
#ifdef SELF_CHECK
  //assert(s1 != 0.0);
#endif
  sign = s1 < 0.0 ? 1 : -1;

  // Test starts from here.
  s1 = orient3d(A, B, R, P) * sign;
  if (s1 < 0.0) {
    // p is in ABR-.
    return DISJOINT;
  }
  s2 = orient3d(B, C, R, P) * sign;
  if (s2 < 0.0) {
    // p is in BCR-.
    return DISJOINT;
  }
  s3 = orient3d(C, A, R, P) * sign;
  if (s3 < 0.0) {
    // p is in CAR-.
    return DISJOINT;
  }
  if (s1 == 0.0) {
    // p is on ABR.
    if (s2 == 0.0) {
      // p is on BCR.
#ifdef SELF_CHECK
      //assert(s3 > 0.0);
#endif
      // p is coincident with b.
      return SHAREVERTEX;
    }
    if (s3 == 0.0) {
      // p is on CAR.
      // p is coincident with a.
      return SHAREVERTEX;
    }
    // p is on edge ab.
    return INTERSECT;
  }
  // p is in ABR+.
  if (s2 == 0.0) {
    // p is on BCR.
    if (s3 == 0.0) {
      // p is on CAR.
      // p is coincident with c.
      return SHAREVERTEX;
    }
    // p is on edge bc.
    return INTERSECT;
  }
  if (s3 == 0.0) {
    // p is on CAR.
    // p is on edge ca.
    return INTERSECT;
  }

  // p is strictly inside abc.
  return INTERSECT;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// tri_edge_cop_inter()    Test whether a triangle (abc) and a coplanar edge //
//                         (pq) are intersecting or not.                     //
//                                                                           //
// A reference point R is required. R is exactly not coplanar with abc and   //
// pq.  Since the caller knows they are coplanar, it must be able to provide //
// (or calculate) such a point.                                              //
//                                                                           //
// The return value indicates one of the four cases: DISJOINT, SHAREVERTEX,  //
// SHAREEDGE, and INTERSECT.                                                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

enum tetgenmesh::interresult tetgenmesh::tri_edge_cop_inter(REAL* A, REAL* B,
  REAL* C, REAL* P, REAL* Q, REAL* R)
{
  enum interresult abpq, bcpq, capq;
  enum interresult abcp, abcq;

  // Test if pq is intersecting one of edges of abc.
  abpq = edge_edge_cop_inter(A, B, P, Q, R);
  if (abpq == INTERSECT || abpq == SHAREEDGE) {
    return abpq;
  }
  bcpq = edge_edge_cop_inter(B, C, P, Q, R);
  if (bcpq == INTERSECT || bcpq == SHAREEDGE) {
    return bcpq;
  }
  capq = edge_edge_cop_inter(C, A, P, Q, R);
  if (capq == INTERSECT || capq == SHAREEDGE) {
    return capq;
  }
  
  // Test if p and q is inside abc.
  abcp = tri_vert_cop_inter(A, B, C, P, R);
  if (abcp == INTERSECT) {
    return INTERSECT;
  }
  abcq = tri_vert_cop_inter(A, B, C, Q, R);
  if (abcq == INTERSECT) {
    return INTERSECT;
  }

  // Combine the test results of edge intersectings and triangle insides
  //   to detect whether abc and pq are sharing vertex or disjointed.
  if (abpq == SHAREVERTEX) {
    // p or q is coincident with a or b.
#ifdef SELF_CHECK
    //assert(abcp ^ abcq);
#endif
    return SHAREVERTEX;
  }
  if (bcpq == SHAREVERTEX) {
    // p or q is coincident with b or c.
#ifdef SELF_CHECK
    //assert(abcp ^ abcq);
#endif
    return SHAREVERTEX;
  }
  if (capq == SHAREVERTEX) {
    // p or q is coincident with c or a.
#ifdef SELF_CHECK
    //assert(abcp ^ abcq);
#endif
    return SHAREVERTEX;
  }

  // They are disjointed.
  return DISJOINT;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// tri_edge_inter_tail()    Test whether a triangle (abc) and an edge (pq)   //
//                          are intersecting or not.                         //
//                                                                           //
// s1 and s2 are results of pre-performed orientation tests. s1 = orient3d(  //
// a, b, c, p); s2 = orient3d(a, b, c, q).  To separate this routine from    //
// tri_edge_inter() can save two orientation tests in tri_tri_inter().       //
//                                                                           //
// The return value indicates one of the four cases: DISJOINT, SHAREVERTEX,  //
// SHAREEDGE, and INTERSECT.                                                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

enum tetgenmesh::interresult tetgenmesh::tri_edge_inter_tail(REAL* A, REAL* B,
  REAL* C, REAL* P, REAL* Q, REAL s1, REAL s2)
{
  REAL s3, s4, s5;
  int sign;

  if (s1 * s2 > 0.0) {
    // p, q are at the same halfspace of ABC, no intersection.
    return DISJOINT;
  }

  if (s1 * s2 < 0.0) {
    // p, q are both not on ABC (and not sharing vertices, edges of abc).
    // Adjust the orientation of a, b, c and p, so that we can assume that
    //   p is strictly in ABC-, and q is strictly in ABC+.
    sign = s1 < 0.0 ? 1 : -1;
    s3 = orient3d(A, B, P, Q) * sign;
    if (s3 < 0.0) {
      // q is at ABP-.
      return DISJOINT;
    }
    s4 = orient3d(B, C, P, Q) * sign;
    if (s4 < 0.0) {
      // q is at BCP-.
      return DISJOINT;
    }
    s5 = orient3d(C, A, P, Q) * sign;
    if (s5 < 0.0) {
      // q is at CAP-.
      return DISJOINT;
    }
    if (s3 == 0.0) {
      // q is on ABP.
      if (s4 == 0.0) {
        // q is on BCP (and q must in CAP+).
#ifdef SELF_CHECK
        //assert(s5 > 0.0); 
#endif
        // pq intersects abc at vertex b.
        return SHAREVERTEX;
      }
      if (s5 == 0.0) {
        // q is on CAP (and q must in BCP+).
        // pq intersects abc at vertex a.
        return SHAREVERTEX;
      }
      // q in both BCP+ and CAP+.
      // pq crosses ab properly.
      return INTERSECT;
    }
    // q is in ABP+;
    if (s4 == 0.0) {
      // q is on BCP.
      if (s5 == 0.0) {
        // q is on CAP.
        // pq intersects abc at vertex c.
        return SHAREVERTEX;
      }
      // pq crosses bc properly.
      return INTERSECT;
    }
    // q is in BCP+;
    if (s5 == 0.0) {
      // q is on CAP.
      // pq crosses ca properly.
      return INTERSECT;
    }
    // q is in CAP+;
    // pq crosses abc properly.
    return INTERSECT;
  }

  if (s1 != 0.0 || s2 != 0.0) {
    // Either p or q is coplanar with abc. ONLY one of them is possible.
    if (s1 == 0.0) {
      // p is coplanar with abc, q can be used as reference point.
#ifdef SELF_CHECK
      //assert(s2 != 0.0);
#endif
      return tri_vert_cop_inter(A, B, C, P, Q);
    } else {
      // q is coplanar with abc, p can be used as reference point.
#ifdef SELF_CHECK
      //assert(s2 == 0.0);
#endif
      return tri_vert_cop_inter(A, B, C, Q, P);
    }
  }

  // pq is coplanar with abc.  Calculate a point which is exactly not
  //   coplanar with a, b, and c.
  REAL R[3], N[3];
  REAL ax, ay, az, bx, by, bz;
  
  ax = A[0] - B[0];
  ay = A[1] - B[1];
  az = A[2] - B[2];
  bx = A[0] - C[0];
  by = A[1] - C[1];
  bz = A[2] - C[2];
  N[0] = ay * bz - by * az;
  N[1] = az * bx - bz * ax;
  N[2] = ax * by - bx * ay;
  // The normal should not be a zero vector (otherwise, abc are collinear).
#ifdef SELF_CHECK
  //assert((fabs(N[0]) + fabs(N[1]) + fabs(N[2])) > 0.0);
#endif
  // The reference point R is lifted from A to the normal direction with
  //   a distance d = average edge length of the triangle abc.
  R[0] = N[0] + A[0];
  R[1] = N[1] + A[1];
  R[2] = N[2] + A[2];
  // Becareful the case: if the non-zero component(s) in N is smaller than
  //   the machine epsilon (i.e., 2^(-16) for double), R will exactly equal
  //   to A due to the round-off error.  Do check if it is.
  if (R[0] == A[0] && R[1] == A[1] && R[2] == A[2]) {
    int i, j;
    for (i = 0; i < 3; i++) {
#ifdef SELF_CHECK
      //assert (R[i] == A[i]);
#endif
      j = 2;
      do {
        if (N[i] > 0.0) {
          N[i] += (j * macheps);
        } else {
          N[i] -= (j * macheps);
        }
        R[i] = N[i] + A[i];
        j *= 2;
      } while (R[i] == A[i]);
    }
  }

  return tri_edge_cop_inter(A, B, C, P, Q, R);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// tri_edge_inter()    Test whether a triangle (abc) and an edge (pq) are    //
//                     intersecting or not.                                  //
//                                                                           //
// The return value indicates one of the four cases: DISJOINT, SHAREVERTEX,  //
// SHAREEDGE, and INTERSECT.                                                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

enum tetgenmesh::interresult tetgenmesh::tri_edge_inter(REAL* A, REAL* B,
  REAL* C, REAL* P, REAL* Q)
{
  REAL s1, s2;

  // Test the locations of p and q with respect to ABC.
  s1 = orient3d(A, B, C, P);
  s2 = orient3d(A, B, C, Q);

  return tri_edge_inter_tail(A, B, C, P, Q, s1, s2);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// tri_tri_inter()    Test whether two triangle (abc) and (opq) are          //
//                    intersecting or not.                                   //
//                                                                           //
// The return value indicates one of the five cases: DISJOINT, SHAREVERTEX,  //
// SHAREEDGE, SHAREFACE, and INTERSECT.                                      //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

enum tetgenmesh::interresult tetgenmesh::tri_tri_inter(REAL* A, REAL* B,
  REAL* C, REAL* O, REAL* P, REAL* Q)
{
  REAL s_o, s_p, s_q;
  REAL s_a, s_b, s_c;

  s_o = orient3d(A, B, C, O);
  s_p = orient3d(A, B, C, P);
  s_q = orient3d(A, B, C, Q);
  if ((s_o * s_p > 0.0) && (s_o * s_q > 0.0)) {
    // o, p, q are all in the same halfspace of ABC.
    return DISJOINT;
  }

  s_a = orient3d(O, P, Q, A);
  s_b = orient3d(O, P, Q, B);
  s_c = orient3d(O, P, Q, C);
  if ((s_a * s_b > 0.0) && (s_a * s_c > 0.0)) {
    // a, b, c are all in the same halfspace of OPQ.
    return DISJOINT;
  }

  enum interresult abcop, abcpq, abcqo;
  int shareedge = 0;

  abcop = tri_edge_inter_tail(A, B, C, O, P, s_o, s_p);
  if (abcop == INTERSECT) {
    return INTERSECT;
  } else if (abcop == SHAREEDGE) {
    shareedge++;
  }
  abcpq = tri_edge_inter_tail(A, B, C, P, Q, s_p, s_q);
  if (abcpq == INTERSECT) {
    return INTERSECT;
  } else if (abcpq == SHAREEDGE) {
    shareedge++;
  }
  abcqo = tri_edge_inter_tail(A, B, C, Q, O, s_q, s_o);
  if (abcqo == INTERSECT) {
    return INTERSECT;
  } else if (abcqo == SHAREEDGE) {
    shareedge++;
  }
  if (shareedge == 3) {
    // opq are coincident with abc.
    return SHAREFACE;
  }
#ifdef SELF_CHECK
  // It is only possible either no share edge or one.
  //assert(shareedge == 0 || shareedge == 1);
#endif

  // Continue to detect whether opq and abc are intersecting or not.
  enum interresult opqab, opqbc, opqca;

  opqab = tri_edge_inter_tail(O, P, Q, A, B, s_a, s_b);
  if (opqab == INTERSECT) {
    return INTERSECT;
  }
  opqbc = tri_edge_inter_tail(O, P, Q, B, C, s_b, s_c);
  if (opqbc == INTERSECT) {
    return INTERSECT;
  }
  opqca = tri_edge_inter_tail(O, P, Q, C, A, s_c, s_a);
  if (opqca == INTERSECT) {
    return INTERSECT;
  }

  // At this point, two triangles are not intersecting and not coincident.
  //   They may be share an edge, or share a vertex, or disjoint.
  if (abcop == SHAREEDGE) {
#ifdef SELF_CHECK
    //assert(abcpq == SHAREVERTEX && abcqo == SHAREVERTEX);
#endif
    // op is coincident with an edge of abc.
    return SHAREEDGE;
  }
  if (abcpq == SHAREEDGE) {
#ifdef SELF_CHECK
    //assert(abcop == SHAREVERTEX && abcqo == SHAREVERTEX);
#endif
    // pq is coincident with an edge of abc.
    return SHAREEDGE;
  }
  if (abcqo == SHAREEDGE) {
#ifdef SELF_CHECK
    //assert(abcop == SHAREVERTEX && abcpq == SHAREVERTEX);
#endif
    // qo is coincident with an edge of abc.
    return SHAREEDGE;
  }

  // They may share a vertex or disjoint.
  if (abcop == SHAREVERTEX) {
    // o or p is coincident with a vertex of abc.
    if (abcpq == SHAREVERTEX) {
      // p is the coincident vertex.
#ifdef SELF_CHECK
      //assert(abcqo != SHAREVERTEX);
#endif
    } else {
      // o is the coincident vertex.
#ifdef SELF_CHECK
      //assert(abcqo == SHAREVERTEX);
#endif
    }
    return SHAREVERTEX;
  }
  if (abcpq == SHAREVERTEX) {
    // q is the coincident vertex.
#ifdef SELF_CHECK
    //assert(abcqo == SHAREVERTEX);
#endif
    return SHAREVERTEX;
  }

  // They are disjoint.
  return DISJOINT;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// tri_edge_2d()    Triangle-edge coplanar intersection test.                //
//                                                                           //
// This routine takes a triangle T (with vertices A, B, C) and an edge E (P, //
// Q) in a plane in 3D, and tests if they intersect each other.  Return 1 if //
// they are intersected, i.e., T \cap E is not empty, otherwise, return 0.   //
//                                                                           //
// If the point 'R' is not NULL, it lies strictly above T [A, B, C].         //
//                                                                           //
// If T1 and T2 intersect each other (return 1), they may intersect in diff- //
// erent ways. If 'level' > 0, their intersection type will be reported in   //
// combinations of 'types' and 'pos'.                                        //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::tri_edge_2d(point A, point B, point C, point P, point Q, 
  point R, int level, int *types, int *pos)
{
  point U[3], V[3];  // The permuted vectors of points.
  int pu[3], pv[3];  // The original positions of points.
  REAL sA, sB, sC;
  REAL s1, s2, s3, s4;
  int z1;

  if (R == NULL) {
    REAL n[3], len;
    // Calculate a lift point, saved in dummypoint.
    facenormal2(A, B, C, n, 1);
    len = sqrt(DOT(n, n));
    n[0] /= len;
    n[1] /= len;
    n[2] /= len;
    len = DIST(A, B);
    len += DIST(B, C);
    len += DIST(C, A);
    len /= 3.0;
    R = dummypoint;
    R[0] = A[0] + len * n[0];
    R[1] = A[1] + len * n[1];
    R[2] = A[2] + len * n[2];
  }

  // Test A's, B's, and C's orientations wrt plane PQR. 
  sA = orient3d(P, Q, R, A);
  sB = orient3d(P, Q, R, B);
  sC = orient3d(P, Q, R, C);
  orient3dcount+=3;

  if (b->verbose > 2) {
    //printf("      Tri-edge-2d (%d %d %d)-(%d %d)-(%d) (%c%c%c)", pointmark(A),
      //pointmark(B), pointmark(C), pointmark(P), pointmark(Q), pointmark(R),
     // sA > 0 ? '+' : (sA < 0 ? '-' : '0'), sB>0 ? '+' : (sB<0 ? '-' : '0'),
      //sC>0 ? '+' : (sC<0 ? '-' : '0'));
  }
  // triedgcopcount++;

  if (sA < 0) {
    if (sB < 0) {
      if (sC < 0) { // (---).
        return 0; 
      } else {
        if (sC > 0) { // (--+).
          // All points are in the right positions.
          SETVECTOR3(U, A, B, C);  // I3
          SETVECTOR3(V, P, Q, R);  // I2
          SETVECTOR3(pu, 0, 1, 2);
          SETVECTOR3(pv, 0, 1, 2);
          z1 = 0;
        } else { // (--0).
          SETVECTOR3(U, A, B, C);  // I3
          SETVECTOR3(V, P, Q, R);  // I2
          SETVECTOR3(pu, 0, 1, 2);
          SETVECTOR3(pv, 0, 1, 2);
          z1 = 1;
        }
      }
    } else { 
      if (sB > 0) {
        if (sC < 0) { // (-+-).
          SETVECTOR3(U, C, A, B);  // PT = ST
          SETVECTOR3(V, P, Q, R);  // I2
          SETVECTOR3(pu, 2, 0, 1);
          SETVECTOR3(pv, 0, 1, 2);
          z1 = 0;
        } else {
          if (sC > 0) { // (-++).
            SETVECTOR3(U, B, C, A);  // PT = ST x ST
            SETVECTOR3(V, Q, P, R);  // PL = SL
            SETVECTOR3(pu, 1, 2, 0);
            SETVECTOR3(pv, 1, 0, 2);
            z1 = 0;
          } else { // (-+0).
            SETVECTOR3(U, C, A, B);  // PT = ST
            SETVECTOR3(V, P, Q, R);  // I2
            SETVECTOR3(pu, 2, 0, 1);
            SETVECTOR3(pv, 0, 1, 2);
            z1 = 2;
          }
        }
      } else {
        if (sC < 0) { // (-0-).
          SETVECTOR3(U, C, A, B);  // PT = ST
          SETVECTOR3(V, P, Q, R);  // I2
          SETVECTOR3(pu, 2, 0, 1);
          SETVECTOR3(pv, 0, 1, 2);
          z1 = 1;
        } else {
          if (sC > 0) { // (-0+).
            SETVECTOR3(U, B, C, A);  // PT = ST x ST
            SETVECTOR3(V, Q, P, R);  // PL = SL
            SETVECTOR3(pu, 1, 2, 0);
            SETVECTOR3(pv, 1, 0, 2);
            z1 = 2;
          } else { // (-00).
            SETVECTOR3(U, B, C, A);  // PT = ST x ST
            SETVECTOR3(V, Q, P, R);  // PL = SL
            SETVECTOR3(pu, 1, 2, 0);
            SETVECTOR3(pv, 1, 0, 2);
            z1 = 3; 
          }
        }
      }
    }
  } else {
    if (sA > 0) {
      if (sB < 0) {
        if (sC < 0) { // (+--).
          SETVECTOR3(U, B, C, A);  // PT = ST x ST
          SETVECTOR3(V, P, Q, R);  // I2
          SETVECTOR3(pu, 1, 2, 0);
          SETVECTOR3(pv, 0, 1, 2);
          z1 = 0;
        } else {
          if (sC > 0) { // (+-+).
            SETVECTOR3(U, C, A, B);  // PT = ST
            SETVECTOR3(V, Q, P, R);  // PL = SL
            SETVECTOR3(pu, 2, 0, 1);
            SETVECTOR3(pv, 1, 0, 2);
            z1 = 0;
          } else { // (+-0).
            SETVECTOR3(U, C, A, B);  // PT = ST
            SETVECTOR3(V, Q, P, R);  // PL = SL
            SETVECTOR3(pu, 2, 0, 1);
            SETVECTOR3(pv, 1, 0, 2);
            z1 = 2;
          }
        }
      } else { 
        if (sB > 0) {
          if (sC < 0) { // (++-).
            SETVECTOR3(U, A, B, C);  // I3
            SETVECTOR3(V, Q, P, R);  // PL = SL
            SETVECTOR3(pu, 0, 1, 2);
            SETVECTOR3(pv, 1, 0, 2);
            z1 = 0;
          } else {
            if (sC > 0) { // (+++).
              return 0; 
            } else { // (++0).
              SETVECTOR3(U, A, B, C);  // I3
              SETVECTOR3(V, Q, P, R);  // PL = SL
              SETVECTOR3(pu, 0, 1, 2);
              SETVECTOR3(pv, 1, 0, 2);
              z1 = 1; 
            }
          }
        } else { // (+0#)
          if (sC < 0) { // (+0-).
            SETVECTOR3(U, B, C, A);  // PT = ST x ST
            SETVECTOR3(V, P, Q, R);  // I2
            SETVECTOR3(pu, 1, 2, 0);
            SETVECTOR3(pv, 0, 1, 2);
            z1 = 2;
          } else {
            if (sC > 0) { // (+0+).
              SETVECTOR3(U, C, A, B);  // PT = ST
              SETVECTOR3(V, Q, P, R);  // PL = SL
              SETVECTOR3(pu, 2, 0, 1);
              SETVECTOR3(pv, 1, 0, 2);
              z1 = 1;
            } else { // (+00).
              SETVECTOR3(U, B, C, A);  // PT = ST x ST
              SETVECTOR3(V, P, Q, R);  // I2
              SETVECTOR3(pu, 1, 2, 0);
              SETVECTOR3(pv, 0, 1, 2);
              z1 = 3; 
            }
          }
        }
      }
    } else { 
      if (sB < 0) {
        if (sC < 0) { // (0--).
          SETVECTOR3(U, B, C, A);  // PT = ST x ST
          SETVECTOR3(V, P, Q, R);  // I2
          SETVECTOR3(pu, 1, 2, 0);
          SETVECTOR3(pv, 0, 1, 2);
          z1 = 1;
        } else {
          if (sC > 0) { // (0-+).
            SETVECTOR3(U, A, B, C);  // I3
            SETVECTOR3(V, P, Q, R);  // I2
            SETVECTOR3(pu, 0, 1, 2);
            SETVECTOR3(pv, 0, 1, 2);
            z1 = 2;
          } else { // (0-0).
            SETVECTOR3(U, C, A, B);  // PT = ST
            SETVECTOR3(V, Q, P, R);  // PL = SL
            SETVECTOR3(pu, 2, 0, 1);
            SETVECTOR3(pv, 1, 0, 2);
            z1 = 3; 
          }
        }
      } else { 
        if (sB > 0) {
          if (sC < 0) { // (0+-).
            SETVECTOR3(U, A, B, C);  // I3
            SETVECTOR3(V, Q, P, R);  // PL = SL
            SETVECTOR3(pu, 0, 1, 2);
            SETVECTOR3(pv, 1, 0, 2);
            z1 = 2;
          } else {
            if (sC > 0) { // (0++).
              SETVECTOR3(U, B, C, A);  // PT = ST x ST
              SETVECTOR3(V, Q, P, R);  // PL = SL
              SETVECTOR3(pu, 1, 2, 0);
              SETVECTOR3(pv, 1, 0, 2);
              z1 = 1;
            } else { // (0+0).
              SETVECTOR3(U, C, A, B);  // PT = ST
              SETVECTOR3(V, P, Q, R);  // I2
              SETVECTOR3(pu, 2, 0, 1);
              SETVECTOR3(pv, 0, 1, 2);
              z1 = 3; 
            }
          }
        } else { // (00#)
          if (sC < 0) { // (00-).
            SETVECTOR3(U, A, B, C);  // I3
            SETVECTOR3(V, Q, P, R);  // PL = SL
            SETVECTOR3(pu, 0, 1, 2);
            SETVECTOR3(pv, 1, 0, 2);
            z1 = 3; 
          } else {
            if (sC > 0) { // (00+).
              SETVECTOR3(U, A, B, C);  // I3
              SETVECTOR3(V, P, Q, R);  // I2
              SETVECTOR3(pu, 0, 1, 2);
              SETVECTOR3(pv, 0, 1, 2);
              z1 = 3; 
            } else { // (000)
              // Not possible unless ABC is degenerate.
              z1 = 4;
            }
          }
        }
      }
    }
  }

  s1 = orient3d(U[0], U[2], R, V[1]);  // A, C, R, Q
  s2 = orient3d(U[1], U[2], R, V[0]);  // B, C, R, P
  orient3dcount+=2;

  if (b->verbose > 2) {
    //printf("      Tri-edge-2d (%d %d %d)-(%d %d %d) (%d) (%c%c)\n",
      //pointmark(U[0]), pointmark(U[1]), pointmark(U[2]), pointmark(V[0]),
      //pointmark(V[1]), pointmark(V[2]), z1, s1>0 ? '+' : (s1<0 ? '-' : '0'),
     // s2>0 ? '+' : (s2<0 ? '-' : '0'));
  }
  //assert(z1 != 4); // SELF_CHECK

  if (s1 > 0) {
    return 0;
  }
  if (s2 < 0) {
    return 0;
  }

  if (level == 0) {
    return 1;  // They are intersected.
  }

  if (z1 == 1) {
    if (s1 == 0) {  // (0###)
      // C = Q.
      types[0] = (int) SHAREVERTEX;
      pos[0] = pu[2]; // C
      pos[1] = pv[1]; // Q
      types[1] = (int) DISJOINT;
    } else {
      if (s2 == 0) { // (#0##)
        // C = P.
        types[0] = (int) SHAREVERTEX;
        pos[0] = pu[2]; // C
        pos[1] = pv[0]; // P
        types[1] = (int) DISJOINT;
      } else { // (-+##)
        // C in [P, Q].
        types[0] = (int) INTERVERT;
        pos[0] = pu[2]; // C
        pos[1] = pv[0]; // [P, Q]
        types[1] = (int) DISJOINT;
      }
    }
    return 1;
  }

  s3 = orient3d(U[0], U[2], R, V[0]);  // A, C, R, P
  s4 = orient3d(U[1], U[2], R, V[1]);  // B, C, R, Q
  orient3dcount+=2;
      
  if (z1 == 0) {  // (tritri-03)
    if (s1 < 0) {
      if (s3 > 0) {
        //assert(s2 > 0); // SELF_CHECK
        if (s4 > 0) {
          // [P, Q] overlaps [k, l] (-+++).
          types[0] = (int) INTEREDGE;
          pos[0] = pu[2]; // [C, A]
          pos[1] = pv[0]; // [P, Q]
          types[1] = (int) TOUCHFACE;
          pos[2] = 3;     // [A, B, C]
          pos[3] = pv[1]; // Q
        } else {
          if (s4 == 0) {
            // Q = l, [P, Q] contains [k, l] (-++0).
            types[0] = (int) INTEREDGE;
            pos[0] = pu[2]; // [C, A]
            pos[1] = pv[0]; // [P, Q]
            types[1] = (int) TOUCHEDGE;
            pos[2] = pu[1]; // [B, C]
            pos[3] = pv[1]; // Q
          } else { // s4 < 0
            // [P, Q] contains [k, l] (-++-).
            types[0] = (int) INTEREDGE;
            pos[0] = pu[2]; // [C, A]
            pos[1] = pv[0]; // [P, Q]
            types[1] = (int) INTEREDGE;
            pos[2] = pu[1]; // [B, C]
            pos[3] = pv[0]; // [P, Q]
          }
        }
      } else {
        if (s3 == 0) {
          //assert(s2 > 0); // SELF_CHECK
          if (s4 > 0) {
            // P = k, [P, Q] in [k, l] (-+0+).
            types[0] = (int) TOUCHEDGE;
            pos[0] = pu[2]; // [C, A]
            pos[1] = pv[0]; // P
            types[1] = (int) TOUCHFACE;
            pos[2] = 3;     // [A, B, C]
            pos[3] = pv[1]; // Q
          } else {
            if (s4 == 0) {
              // [P, Q] = [k, l] (-+00).
              types[0] = (int) TOUCHEDGE;
              pos[0] = pu[2]; // [C, A]
              pos[1] = pv[0]; // P
              types[1] = (int) TOUCHEDGE;
              pos[2] = pu[1]; // [B, C]
              pos[3] = pv[1]; // Q
            } else {
              // P = k, [P, Q] contains [k, l] (-+0-).
              types[0] = (int) TOUCHEDGE;
              pos[0] = pu[2]; // [C, A]
              pos[1] = pv[0]; // P
              types[1] = (int) INTEREDGE;
              pos[2] = pu[1]; // [B, C]
              pos[3] = pv[0]; // [P, Q]
            }
          }
        } else { // s3 < 0
          if (s2 > 0) {
            if (s4 > 0) {
              // [P, Q] in [k, l] (-+-+).
              types[0] = (int) TOUCHFACE;
              pos[0] = 3;     // [A, B, C]
              pos[1] = pv[0]; // P
              types[1] = (int) TOUCHFACE;
              pos[2] = 3;     // [A, B, C]
              pos[3] = pv[1]; // Q
            } else {
              if (s4 == 0) {
                // Q = l, [P, Q] in [k, l] (-+-0).
                types[0] = (int) TOUCHFACE;
                pos[0] = 3;     // [A, B, C]
                pos[1] = pv[0]; // P
                types[1] = (int) TOUCHEDGE;
                pos[2] = pu[1]; // [B, C]
                pos[3] = pv[1]; // Q
              } else { // s4 < 0
                // [P, Q] overlaps [k, l] (-+--).
                types[0] = (int) TOUCHFACE;
                pos[0] = 3;     // [A, B, C]
                pos[1] = pv[0]; // P
                types[1] = (int) INTEREDGE;
                pos[2] = pu[1]; // [B, C]
                pos[3] = pv[0]; // [P, Q]
              }
            }
          } else { // s2 == 0
            // P = l (#0##).
            types[0] = (int) TOUCHEDGE;
            pos[0] = pu[1]; // [B, C]
            pos[1] = pv[0]; // P
            types[1] = (int) DISJOINT;
          }
        }
      }
    } else { // s1 == 0
      // Q = k (0####)
      types[0] = (int) TOUCHEDGE;
      pos[0] = pu[2]; // [C, A]
      pos[1] = pv[1]; // Q
      types[1] = (int) DISJOINT;
    }
  } else if (z1 == 2) {  // (tritri-23)
    if (s1 < 0) {
      if (s3 > 0) {
        //assert(s2 > 0); // SELF_CHECK
        if (s4 > 0) {
          // [P, Q] overlaps [A, l] (-+++).
          types[0] = (int) INTERVERT;
          pos[0] = pu[0]; // A
          pos[1] = pv[0]; // [P, Q]
          types[1] = (int) TOUCHFACE;
          pos[2] = 3;     // [A, B, C]
          pos[3] = pv[1]; // Q
        } else {
          if (s4 == 0) {
            // Q = l, [P, Q] contains [A, l] (-++0).
            types[0] = (int) INTERVERT;
            pos[0] = pu[0]; // A
            pos[1] = pv[0]; // [P, Q]
            types[1] = (int) TOUCHEDGE;
            pos[2] = pu[1]; // [B, C]
            pos[3] = pv[1]; // Q
          } else { // s4 < 0
            // [P, Q] contains [A, l] (-++-).
            types[0] = (int) INTERVERT;
            pos[0] = pu[0]; // A
            pos[1] = pv[0]; // [P, Q]
            types[1] = (int) INTEREDGE;
            pos[2] = pu[1]; // [B, C]
            pos[3] = pv[0]; // [P, Q]
          }
        }
      } else {
        if (s3 == 0) {
          //assert(s2 > 0); // SELF_CHECK
          if (s4 > 0) {
            // P = A, [P, Q] in [A, l] (-+0+).
            types[0] = (int) SHAREVERTEX;
            pos[0] = pu[0]; // A
            pos[1] = pv[0]; // P
            types[1] = (int) TOUCHFACE;
            pos[2] = 3;     // [A, B, C]
            pos[3] = pv[1]; // Q
          } else {
            if (s4 == 0) {
              // [P, Q] = [A, l] (-+00).
              types[0] = (int) SHAREVERTEX;
              pos[0] = pu[0]; // A
              pos[1] = pv[0]; // P
              types[1] = (int) TOUCHEDGE;
              pos[2] = pu[1]; // [B, C]
              pos[3] = pv[1]; // Q
            } else { // s4 < 0
              // Q = l, [P, Q] in [A, l] (-+0-).
              types[0] = (int) SHAREVERTEX;
              pos[0] = pu[0]; // A
              pos[1] = pv[0]; // P
              types[1] = (int) INTEREDGE;
              pos[2] = pu[1]; // [B, C]
              pos[3] = pv[0]; // [P, Q]
            }
          }
        } else { // s3 < 0
          if (s2 > 0) {
            if (s4 > 0) {
              // [P, Q] in [A, l] (-+-+).
              types[0] = (int) TOUCHFACE;
              pos[0] = 3;     // [A, B, C]
              pos[1] = pv[0]; // P
              types[0] = (int) TOUCHFACE;
              pos[0] = 3;     // [A, B, C]
              pos[1] = pv[1]; // Q
            } else {
              if (s4 == 0) {
                // Q = l, [P, Q] in [A, l] (-+-0).
                types[0] = (int) TOUCHFACE;
                pos[0] = 3;     // [A, B, C]
                pos[1] = pv[0]; // P
                types[0] = (int) TOUCHEDGE;
                pos[0] = pu[1]; // [B, C]
                pos[1] = pv[1]; // Q
              } else { // s4 < 0
                // [P, Q] overlaps [A, l] (-+--).
                types[0] = (int) TOUCHFACE;
                pos[0] = 3;     // [A, B, C]
                pos[1] = pv[0]; // P
                types[0] = (int) INTEREDGE;
                pos[0] = pu[1]; // [B, C]
                pos[1] = pv[0]; // [P, Q]
              }
            }
          } else { // s2 == 0
            // P = l (#0##).
            types[0] = (int) TOUCHEDGE;
            pos[0] = pu[1]; // [B, C]
            pos[1] = pv[0]; // P
            types[1] = (int) DISJOINT;
          }
        }
      }
    } else { // s1 == 0
      // Q = A (0###).
      types[0] = (int) SHAREVERTEX;
      pos[0] = pu[0]; // A
      pos[1] = pv[1]; // Q
      types[1] = (int) DISJOINT;
    }
  } else if (z1 == 3) {  // (tritri-33)
    if (s1 < 0) {
      if (s3 > 0) {
        //assert(s2 > 0); // SELF_CHECK
        if (s4 > 0) {
          // [P, Q] overlaps [A, B] (-+++).
          types[0] = (int) INTERVERT;
          pos[0] = pu[0]; // A
          pos[1] = pv[0]; // [P, Q]
          types[1] = (int) TOUCHEDGE;
          pos[2] = pu[0]; // [A, B]
          pos[3] = pv[1]; // Q
        } else {
          if (s4 == 0) {
            // Q = B, [P, Q] contains [A, B] (-++0).
            types[0] = (int) INTERVERT;
            pos[0] = pu[0]; // A
            pos[1] = pv[0]; // [P, Q]
            types[1] = (int) SHAREVERTEX;
            pos[2] = pu[1]; // B
            pos[3] = pv[1]; // Q
          } else { // s4 < 0
            // [P, Q] contains [A, B] (-++-).
            types[0] = (int) INTERVERT;
            pos[0] = pu[0]; // A
            pos[1] = pv[0]; // [P, Q]
            types[1] = (int) INTERVERT;
            pos[2] = pu[1]; // B
            pos[3] = pv[0]; // [P, Q]
          }
        }
      } else {
        if (s3 == 0) {
          //assert(s2 > 0); // SELF_CHECK
          if (s4 > 0) {
            // P = A, [P, Q] in [A, B] (-+0+).
            types[0] = (int) SHAREVERTEX;
            pos[0] = pu[0]; // A
            pos[1] = pv[0]; // P
            types[1] = (int) TOUCHEDGE;
            pos[2] = pu[0]; // [A, B]
            pos[3] = pv[1]; // Q
          } else {
            if (s4 == 0) {
              // [P, Q] = [A, B] (-+00).
              types[0] = (int) SHAREEDGE;
              pos[0] = pu[0]; // [A, B]
              pos[1] = pv[0]; // [P, Q]
              types[1] = (int) DISJOINT;
            } else { // s4 < 0
              // P= A, [P, Q] in [A, B] (-+0-).
              types[0] = (int) SHAREVERTEX;
              pos[0] = pu[0]; // A
              pos[1] = pv[0]; // P
              types[1] = (int) INTERVERT;
              pos[2] = pu[1]; // B
              pos[3] = pv[0]; // [P, Q]
            }
          }
        } else { // s3 < 0
          if (s2 > 0) {
            if (s4 > 0) {
              // [P, Q] in [A, B] (-+-+).
              types[0] = (int) TOUCHEDGE;
              pos[0] = pu[0]; // [A, B]
              pos[1] = pv[0]; // P
              types[1] = (int) TOUCHEDGE;
              pos[2] = pu[0]; // [A, B]
              pos[3] = pv[1]; // Q
            } else {
              if (s4 == 0) {
                // Q = B, [P, Q] in [A, B] (-+-0).
                types[0] = (int) TOUCHEDGE;
                pos[0] = pu[0]; // [A, B]
                pos[1] = pv[0]; // P
                types[1] = (int) SHAREVERTEX;
                pos[2] = pu[1]; // B
                pos[3] = pv[1]; // Q
              } else { // s4 < 0
                // [P, Q] overlaps [A, B] (-+--).
                types[0] = (int) TOUCHEDGE;
                pos[0] = pu[0]; // [A, B]
                pos[1] = pv[0]; // P
                types[1] = (int) INTERVERT;
                pos[2] = pu[1]; // B
                pos[3] = pv[0]; // [P, Q]
              }
            }
          } else { // s2 == 0
            // P = B (#0##).
            types[0] = (int) SHAREVERTEX;
            pos[0] = pu[1]; // B
            pos[1] = pv[0]; // P
            types[1] = (int) DISJOINT;
          }
        }
      }
    } else { // s1 == 0
      // Q = A (0###).
      types[0] = (int) SHAREVERTEX;
      pos[0] = pu[0]; // A
      pos[1] = pv[1]; // Q
      types[1] = (int) DISJOINT;
    }
  }

  return 1;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// tri_edge_test()    Triangle-edge intersection test.                       //
//                                                                           //
// This routine takes a triangle T (with vertices A, B, C) and an edge E (P, //
// Q) in 3D, and tests if they intersect each other.  Return 1 if they are   //
// intersected, i.e., T \cap E is not empty, otherwise, return 0.            //
//                                                                           //
// If the point 'R' is not NULL, it lies strictly above the plane defined by //
// A, B, C. It is used in test when T and E are coplanar.                    //
//                                                                           //
// If T1 and T2 intersect each other (return 1), they may intersect in diff- //
// erent ways. If 'level' > 0, their intersection type will be reported in   //
// combinations of 'types' and 'pos'.                                        //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::tri_edge_test(point A, point B, point C, point P, point Q, 
  point R, int level, int *types, int *pos)
{
  point U[3], V[3]; //, Ptmp;
  int pu[3], pv[3]; //, itmp;
  REAL sP, sQ, s1, s2, s3;
  int z1;

  // Test the locations of P and Q with respect to ABC.
  sP = orient3d(A, B, C, P);
  sQ = orient3d(A, B, C, Q);
  orient3dcount+=2;

  if (b->verbose > 2) {
    //printf("      Tri-edge (%d %d %d)-(%d %d) (%c%c).\n", pointmark(A),
      //pointmark(B), pointmark(C), pointmark(P), pointmark(Q),
      //sP>0 ? '+' : (sP<0 ? '-' : '0'), sQ>0 ? '+' : (sQ<0 ? '-' : '0'));
  }
  // triedgcount++;

  if (sP < 0) {
    if (sQ < 0) { // (--) disjoint
      return 0;
    } else {
      if (sQ > 0) { // (-+)
        SETVECTOR3(U, A, B, C);
        SETVECTOR3(V, P, Q, R);
        SETVECTOR3(pu, 0, 1, 2);
        SETVECTOR3(pv, 0, 1, 2);
        z1 = 0;
      } else { // (-0)
        SETVECTOR3(U, A, B, C);
        SETVECTOR3(V, P, Q, R);
        SETVECTOR3(pu, 0, 1, 2);
        SETVECTOR3(pv, 0, 1, 2);
        z1 = 1;
      }
    }
  } else {
    if (sP > 0) { // (+-)
      if (sQ < 0) {
        SETVECTOR3(U, A, B, C);
        SETVECTOR3(V, Q, P, R);  // P and Q are flipped.
        SETVECTOR3(pu, 0, 1, 2);
        SETVECTOR3(pv, 1, 0, 2);
        z1 = 0;
      } else {
        if (sQ > 0) { // (++) disjoint
          return 0;
        } else { // (+0)
          SETVECTOR3(U, B, A, C); // A and B are flipped.
          SETVECTOR3(V, P, Q, R);
          SETVECTOR3(pu, 1, 0, 2);
          SETVECTOR3(pv, 0, 1, 2);
          z1 = 1;
        }
      }
    } else { // sP == 0
      if (sQ < 0) { // (0-)
        SETVECTOR3(U, A, B, C);
        SETVECTOR3(V, Q, P, R);  // P and Q are flipped.
        SETVECTOR3(pu, 0, 1, 2);
        SETVECTOR3(pv, 1, 0, 2);
        z1 = 1;
      } else {
        if (sQ > 0) { // (0+)
          SETVECTOR3(U, B, A, C);  // A and B are flipped.
          SETVECTOR3(V, Q, P, R);  // P and Q are flipped.
          SETVECTOR3(pu, 1, 0, 2);
          SETVECTOR3(pv, 1, 0, 2);
          z1 = 1;
        } else { // (00)
          // A, B, C, P, and Q are coplanar.
          z1 = 2;
        }
      }
    }
  }

  if (z1 == 2) {
    // The triangle and the edge are coplanar.
    return tri_edge_2d(A, B, C, P, Q, R, level, types, pos);
  }

  s1 = orient3d(U[0], U[1], V[0], V[1]); orient3dcount++;
  if (s1 < 0) {
    return 0;
  }

  s2 = orient3d(U[1], U[2], V[0], V[1]); orient3dcount++;
  if (s2 < 0) {
    return 0;
  }

  s3 = orient3d(U[2], U[0], V[0], V[1]); orient3dcount++;
  if (s3 < 0) {
    return 0;
  }

  if (b->verbose > 2) {
    //printf("      Tri-edge (%d %d %d)-(%d %d) (%c%c%c).\n", pointmark(U[0]),
      //pointmark(U[1]), pointmark(U[2]), pointmark(V[0]), pointmark(V[1]),
      //s1>0 ? '+' : (s1<0 ? '-' : '0'), s2>0 ? '+' : (s2<0 ? '-' : '0'),
      //s3>0 ? '+' : (s3<0 ? '-' : '0'));
  }

  if (level == 0) {
    return 1;  // The are intersected.
  }

  types[1] = (int) DISJOINT; // No second intersection point.

  if (z1 == 0) {
    if (s1 > 0) {
      if (s2 > 0) {
        if (s3 > 0) { // (+++)
          // [P, Q] passes interior of [A, B, C].
          types[0] = (int) INTERFACE;
          pos[0] = 3;  // interior of [A, B, C]
          pos[1] = 0;  // [P, Q]
        } else { // s3 == 0 (++0)
          // [P, Q] intersects [C, A].
          types[0] = (int) INTEREDGE;
          pos[0] = pu[2];  // [C, A]
          pos[1] = 0;  // [P, Q]
        }
      } else { // s2 == 0
        if (s3 > 0) { // (+0+)
          // [P, Q] intersects [B, C].
          types[0] = (int) INTEREDGE;
          pos[0] = pu[1];  // [B, C]
          pos[1] = 0;  // [P, Q]
        } else { // s3 == 0 (+00)
          // [P, Q] passes C.
          types[0] = (int) INTERVERT;
          pos[0] = pu[2];  // C
          pos[1] = 0;  // [P, Q]
        }
      }
    } else { // s1 == 0
      if (s2 > 0) {
        if (s3 > 0) { // (0++)
          // [P, Q] intersects [A, B].
          types[0] = (int) INTEREDGE;
          pos[0] = pu[0];  // [A, B]
          pos[1] = 0;  // [P, Q]
        } else { // s3 == 0 (0+0)
          // [P, Q] passes A.
          types[0] = (int) INTERVERT;
          pos[0] = pu[0];  // A
          pos[1] = 0;  // [P, Q]
        }
      } else { // s2 == 0
        if (s3 > 0) { // (00+)
          // [P, Q] passes B.
          types[0] = (int) INTERVERT;
          pos[0] = pu[1];  // B
          pos[1] = 0;  // [P, Q]
        } else { // s3 == 0 (000)
          // Impossible.
          //assert(0);
        }
      }
    }
  } else { // z1 == 1
    if (s1 > 0) {
      if (s2 > 0) {
        if (s3 > 0) { // (+++)
          // Q lies in [A, B, C].
          types[0] = (int) TOUCHFACE;
          pos[0] = 0; // [A, B, C]
          pos[1] = pv[1]; // Q
        } else { // s3 == 0 (++0)
          // Q lies on [C, A].
          types[0] = (int) TOUCHEDGE;
          pos[0] = pu[2]; // [C, A]
          pos[1] = pv[1]; // Q
        }
      } else { // s2 == 0
        if (s3 > 0) { // (+0+)
          // Q lies on [B, C].
          types[0] = (int) TOUCHEDGE;
          pos[0] = pu[1]; // [B, C]
          pos[1] = pv[1]; // Q
        } else { // s3 == 0 (+00)
          // Q = C.
          types[0] = (int) SHAREVERTEX;
          pos[0] = pu[2]; // C
          pos[1] = pv[1]; // Q
        }
      }
    } else { // s1 == 0
      if (s2 > 0) {
        if (s3 > 0) { // (0++)
          // Q lies on [A, B].
          types[0] = (int) TOUCHEDGE;
          pos[0] = pu[0]; // [A, B]
          pos[1] = pv[1]; // Q
        } else { // s3 == 0 (0+0)
          // Q = A.
          types[0] = (int) SHAREVERTEX;
          pos[0] = pu[0]; // A
          pos[1] = pv[1]; // Q
        }
      } else { // s2 == 0
        if (s3 > 0) { // (00+)
          // Q = B.
          types[0] = (int) SHAREVERTEX;
          pos[0] = pu[1]; // B
          pos[1] = pv[1]; // Q
        } else { // s3 == 0 (000)
          // Impossible.
          //assert(0);
        }
      }
    }
  }

  return 1;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// incircle3d()    3D in-circle test.                                        //
//                                                                           //
// Return a negative value if pd is inside the circumcircle of the triangle  //
// pa, pb, and pc.                                                           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

REAL tetgenmesh::incircle3d(point pa, point pb, point pc, point pd)
{
  REAL area2[2], n1[3], n2[3], c[3];
  REAL sign, r, d;

  // Calculate the areas of the two triangles [a, b, c] and [b, a, d].
  facenormal2(pa, pb, pc, n1, 1);
  area2[0] = DOT(n1, n1);
  facenormal2(pb, pa, pd, n2, 1);
  area2[1] = DOT(n2, n2);

  if (area2[0] > area2[1]) {
    // Choose [a, b, c] as the base triangle.
    circumsphere(pa, pb, pc, NULL, c, &r);
    d = DIST(c, pd);
  } else {
    // Choose [b, a, d] as the base triangle.
    if (area2[1] > 0) {
      circumsphere(pb, pa, pd, NULL, c, &r);
      d = DIST(c, pc);
    } else {
      // The four points are collinear. This case only happens on the boundary.
      return 0; // Return "not inside".
    }
  }

  sign = d - r;
  if (fabs(sign) / r < b->epsilon) {
    sign = 0;
  }

  return sign;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// insphere_s()    Insphere test with symbolic perturbation.                 //
//                                                                           //
// Given four points pa, pb, pc, and pd, test if the point pe lies inside or //
// outside the circumscirbed sphere of the four points.  Here we assume that //
// the orientation of the sequence {pa, pb, pc, pd} is negative (NOT zero),  //
// i.e., pd lies at the negative side of the plane defined by pa, pb, and pc.//
//                                                                           //
// Return a positive value (> 0) if pe lies outside, a negative value (< 0)  //
// if pe lies inside the sphere, the returned value will not be zero.        //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

REAL tetgenmesh::insphere_s(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* pe)
{
  REAL sign;

  inspherecount++;

  sign = insphere(pa, pb, pc, pd, pe);
  if (sign != 0.0) {
    return sign;
  }

  insphere_sos_count++;

  // Symbolic perturbation.
  point pt[5], swappt;
  REAL oriA, oriB;
  int swaps, count;
  int n, i;

  pt[0] = pa;
  pt[1] = pb;
  pt[2] = pc;
  pt[3] = pd;
  pt[4] = pe;
  
  // Sort the five points such that their indices are in the increasing
  //   order. An optimized bubble sort algorithm is used, i.e., it has
  //   the worst case O(n^2) runtime, but it is usually much faster.
  swaps = 0; // Record the total number of swaps.
  n = 5;
  do {
    count = 0;
    n = n - 1;
    for (i = 0; i < n; i++) {
      if (pointmark(pt[i]) > pointmark(pt[i+1])) {
        swappt = pt[i]; pt[i] = pt[i+1]; pt[i+1] = swappt;
        count++;
      }
    }
    swaps += count;
  } while (count > 0); // Continue if some points are swapped.

  oriA = orient3d(pt[1], pt[2], pt[3], pt[4]);
  if (oriA != 0.0) {
    // Flip the sign if there are odd number of swaps.
    if ((swaps % 2) != 0) oriA = -oriA;
    return oriA;
  }
  
  oriB = -orient3d(pt[0], pt[2], pt[3], pt[4]);
  //assert(oriB != 0.0); // SELF_CHECK
  // Flip the sign if there are odd number of swaps.
  if ((swaps % 2) != 0) oriB = -oriB;
  return oriB;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// iscollinear()    Check if three points are approximately collinear.       //
//                                                                           //
// 'eps' is a relative error tolerance.  The collinearity is determined by   //
// the value q = cos(theta), where theta is the angle between two vectors    //
// A->B and A->C.  They're collinear if 1.0 - q <= epspp.                    //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

__device__ bool tetgenmesh::iscollinear(REAL* A, REAL* B, REAL* C, REAL eps)
{
  REAL abx, aby, abz;
  REAL acx, acy, acz;
  REAL Lv, Lw, dd;
  REAL d, q;

  // Limit of two closed points.
  q = longest * eps;
  q *= q;

  abx = A[0] - B[0];
  aby = A[1] - B[1];
  abz = A[2] - B[2];
  acx = A[0] - C[0];
  acy = A[1] - C[1];
  acz = A[2] - C[2];
  Lv = abx * abx + aby * aby + abz * abz;
  // Is AB (nearly) indentical?
  if (Lv < q) return true;
  Lw = acx * acx + acy * acy + acz * acz;
  // Is AC (nearly) indentical?
  if (Lw < q) return true;
  dd = abx * acx + aby * acy + abz * acz;
  
  d = (dd * dd) / (Lv * Lw);
  if (d > 1.0) d = 1.0; // Rounding.
  q = 1.0 - sqrt(d); // Notice 0 < q < 1.0.
  
  return q <= eps;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// iscoplanar()    Check if four points are approximately coplanar.          //
//                                                                           //
// 'vol6' is six times of the signed volume of the tetrahedron formed by the //
// four points. 'eps' is the relative error tolerance.  The coplanarity is   //
// determined by the value: q = fabs(vol6) / L^3,  where L is the average    //
// edge length of the tet. They're coplanar if q <= eps.                     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::
iscoplanar(REAL* k, REAL* l, REAL* m, REAL* n, REAL vol6, REAL eps)
{
  REAL L, q;
  REAL x, y, z;  

  if (vol6 == 0.0) return true;

  x = k[0] - l[0];
  y = k[1] - l[1];
  z = k[2] - l[2];
  L = sqrt(x * x + y * y + z * z);
  x = l[0] - m[0];
  y = l[1] - m[1];
  z = l[2] - m[2];
  L += sqrt(x * x + y * y + z * z);
  x = m[0] - k[0];
  y = m[1] - k[1];
  z = m[2] - k[2];
  L += sqrt(x * x + y * y + z * z);
  x = k[0] - n[0];
  y = k[1] - n[1];
  z = k[2] - n[2];
  L += sqrt(x * x + y * y + z * z);
  x = l[0] - n[0];
  y = l[1] - n[1];
  z = l[2] - n[2];
  L += sqrt(x * x + y * y + z * z);
  x = m[0] - n[0];
  y = m[1] - n[1];
  z = m[2] - n[2];
  L += sqrt(x * x + y * y + z * z);
#ifdef SELF_CHECK
  //assert(L > 0.0);
#endif
  L /= 6.0;
  q = fabs(vol6) / (L * L * L);
  
  return q <= eps;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// iscospheric()    Check if five points are approximately coplanar.         //
//                                                                           //
// 'vol24' is the 24 times of the signed volume of the 4-dimensional simplex //
// formed by the five points. 'eps' is the relative tolerance. The cosphere  //
// case is determined by the value: q = fabs(vol24) / L^4,  where L is the   //
// average edge length of the simplex. They're cosphere if q <= eps.         //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::
iscospheric(REAL* k, REAL* l, REAL* m, REAL* n, REAL* o, REAL vol24, REAL eps)
{
  REAL L, q;

  // A 4D simplex has 10 edges.
  L = distance(k, l);
  L += distance(l, m);
  L += distance(m, k);
  L += distance(k, n);
  L += distance(l, n);
  L += distance(m, n);
  L += distance(k, o);
  L += distance(l, o);
  L += distance(m, o);
  L += distance(n, o);
#ifdef SELF_CHECK
  //assert(L > 0.0);
#endif
  L /= 10.0;
  q = fabs(vol24) / (L * L * L * L);

  return q < eps;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// lu_decmp()    Compute the LU decomposition of a matrix.                   //
//                                                                           //
// Compute the LU decomposition of a (non-singular) square matrix A using    //
// partial pivoting and implicit row exchanges.  The result is:              //
//     A = P * L * U,                                                        //
// where P is a permutation matrix, L is unit lower triangular, and U is     //
// upper triangular.  The factored form of A is used in combination with     //
// 'lu_solve()' to solve linear equations: Ax = b, or invert a matrix.       //
//                                                                           //
// The inputs are a square matrix 'lu[N..n+N-1][N..n+N-1]', it's size is 'n'.//
// On output, 'lu' is replaced by the LU decomposition of a rowwise permuta- //
// tion of itself, 'ps[N..n+N-1]' is an output vector that records the row   //
// permutation effected by the partial pivoting, effectively,  'ps' array    //
// tells the user what the permutation matrix P is; 'd' is output as +1/-1   //
// depending on whether the number of row interchanges was even or odd,      //
// respectively.                                                             //
//                                                                           //
// Return true if the LU decomposition is successfully computed, otherwise,  //
// return false in case that A is a singular matrix.                         //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::lu_decmp(REAL lu[4][4], int n, int* ps, REAL* d, int N)
{
  REAL scales[4];
  REAL pivot, biggest, mult, tempf;
  int pivotindex = 0;
  int i, j, k;

  *d = 1.0;                                      // No row interchanges yet.

  for (i = N; i < n + N; i++) {                             // For each row.
    // Find the largest element in each row for row equilibration
    biggest = 0.0;
    for (j = N; j < n + N; j++)
      if (biggest < (tempf = fabs(lu[i][j])))
        biggest  = tempf;
    if (biggest != 0.0)
      scales[i] = 1.0 / biggest;
    else {
      scales[i] = 0.0;
      return false;                            // Zero row: singular matrix.
    }
    ps[i] = i;                                 // Initialize pivot sequence.
  }

  for (k = N; k < n + N - 1; k++) {                      // For each column.
    // Find the largest element in each column to pivot around.
    biggest = 0.0;
    for (i = k; i < n + N; i++) {
      if (biggest < (tempf = fabs(lu[ps[i]][k]) * scales[ps[i]])) {
        biggest = tempf;
        pivotindex = i;
      }
    }
    if (biggest == 0.0) {
      return false;                         // Zero column: singular matrix.
    }
    if (pivotindex != k) {                         // Update pivot sequence.
      j = ps[k];
      ps[k] = ps[pivotindex];
      ps[pivotindex] = j;
      *d = -(*d);                          // ...and change the parity of d.
    }

    // Pivot, eliminating an extra variable  each time
    pivot = lu[ps[k]][k];
    for (i = k + 1; i < n + N; i++) {
      lu[ps[i]][k] = mult = lu[ps[i]][k] / pivot;
      if (mult != 0.0) {
        for (j = k + 1; j < n + N; j++)
          lu[ps[i]][j] -= mult * lu[ps[k]][j];
      }
    }
  }

  // (lu[ps[n + N - 1]][n + N - 1] == 0.0) ==> A is singular.
  return lu[ps[n + N - 1]][n + N - 1] != 0.0;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// lu_solve()    Solves the linear equation:  Ax = b,  after the matrix A    //
//               has been decomposed into the lower and upper triangular     //
//               matrices L and U, where A = LU.                             //
//                                                                           //
// 'lu[N..n+N-1][N..n+N-1]' is input, not as the matrix 'A' but rather as    //
// its LU decomposition, computed by the routine 'lu_decmp'; 'ps[N..n+N-1]'  //
// is input as the permutation vector returned by 'lu_decmp';  'b[N..n+N-1]' //
// is input as the right-hand side vector, and returns with the solution     //
// vector. 'lu', 'n', and 'ps' are not modified by this routine and can be   //
// left in place for successive calls with different right-hand sides 'b'.   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::lu_solve(REAL lu[4][4], int n, int* ps, REAL* b, int N)
{
  int i, j;
  REAL X[4], dot;

  for (i = N; i < n + N; i++) X[i] = 0.0;

  // Vector reduction using U triangular matrix.
  for (i = N; i < n + N; i++) {
    dot = 0.0;
    for (j = N; j < i + N; j++)
      dot += lu[ps[i]][j] * X[j];
    X[i] = b[ps[i]] - dot;
  }

  // Back substitution, in L triangular matrix.
  for (i = n + N - 1; i >= N; i--) {
    dot = 0.0;
    for (j = i + 1; j < n + N; j++)
      dot += lu[ps[i]][j] * X[j];
    X[i] = (X[i] - dot) / lu[ps[i]][i];
  }

  for (i = N; i < n + N; i++) b[i] = X[i];
}

// initm44() initializes a 4x4 matrix.
static void initm44(REAL a00, REAL a01, REAL a02, REAL a03,
                    REAL a10, REAL a11, REAL a12, REAL a13,
                    REAL a20, REAL a21, REAL a22, REAL a23,
                    REAL a30, REAL a31, REAL a32, REAL a33,
                    REAL M[4][4])
{
  M[0][0] = a00; M[0][1] = a01; M[0][2] = a02; M[0][3] = a03;
  M[1][0] = a10; M[1][1] = a11; M[1][2] = a12; M[1][3] = a13;
  M[2][0] = a20; M[2][1] = a21; M[2][2] = a22; M[2][3] = a23;
  M[3][0] = a30; M[3][1] = a31; M[3][2] = a32; M[3][3] = a33;
}

// m4xv4() multiplies a 4x4 matrix and 4x1 vector: v2 = m * v1
static void m4xv4(REAL v2[4], REAL m[4][4], REAL v1[4])
{
  v2[0] = m[0][0]*v1[0] + m[0][1]*v1[1] + m[0][2]*v1[2] + m[0][3]*v1[3];
  v2[1] = m[1][0]*v1[0] + m[1][1]*v1[1] + m[1][2]*v1[2] + m[1][3]*v1[3];
  v2[2] = m[2][0]*v1[0] + m[2][1]*v1[1] + m[2][2]*v1[2] + m[2][3]*v1[3];
  v2[3] = m[3][0]*v1[0] + m[3][1]*v1[1] + m[3][2]*v1[2] + m[3][3]*v1[3];
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// shortdistance()    Returns the shortest distance from point p to a line   //
//                    defined by two points e1 and e2.                       //
//                                                                           //
// First compute the projection length l_p of the vector v1 = p - e1 along   //
// the vector v2 = e2 - e1. Then Pythagoras' Theorem is used to compute the  //
// shortest distance.                                                        //
//                                                                           //
// This routine allows that p is collinear with the line. In this case, the  //
// return value is zero. The two points e1 and e2 should not be identical.   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

REAL tetgenmesh::shortdistance(REAL* p, REAL* e1, REAL* e2)
{
  REAL v1[3], v2[3];
  REAL len, l_p;

  v1[0] = e2[0] - e1[0];
  v1[1] = e2[1] - e1[1];
  v1[2] = e2[2] - e1[2];
  v2[0] = p[0] - e1[0];
  v2[1] = p[1] - e1[1];
  v2[2] = p[2] - e1[2];

  len = sqrt(dot(v1, v1));
#ifdef SELF_CHECK
  //assert(len != 0.0);
#endif
  v1[0] /= len;
  v1[1] /= len;
  v1[2] /= len;
  l_p = dot(v1, v2);

  return sqrt(dot(v2, v2) - l_p * l_p);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// shortdistance()    Returns the shortest distance from point p to a face.  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

REAL tetgenmesh::shortdistance(REAL* p, REAL* e1, REAL* e2, REAL* e3)
{
  REAL prj[3];

  projpt2face(p, e1, e2, e3, prj);
  return distance(p, prj);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// interiorangle()    Return the interior angle (0 - 2 * PI) between vectors //
//                    o->p1 and o->p2.                                       //
//                                                                           //
// 'n' is the normal of the plane containing face (o, p1, p2).  The interior //
// angle is the total angle rotating from o->p1 around n to o->p2.  Exchange //
// the position of p1 and p2 will get the complement angle of the other one. //
// i.e., interiorangle(o, p1, p2) = 2 * PI - interiorangle(o, p2, p1).  Set  //
// 'n' be NULL if you only want the interior angle between 0 - PI.           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

REAL tetgenmesh::interiorangle(REAL* o, REAL* p1, REAL* p2, REAL* n)
{
  REAL v1[3], v2[3], np[3];
  REAL theta, costheta, lenlen;
  REAL ori, len1, len2;

  // Get the interior angle (0 - PI) between o->p1, and o->p2.
  v1[0] = p1[0] - o[0];
  v1[1] = p1[1] - o[1];
  v1[2] = p1[2] - o[2];
  v2[0] = p2[0] - o[0];
  v2[1] = p2[1] - o[1];
  v2[2] = p2[2] - o[2];
  len1 = sqrt(dot(v1, v1));
  len2 = sqrt(dot(v2, v2));
  lenlen = len1 * len2;
#ifdef SELF_CHECK
  //assert(lenlen != 0.0);
#endif
  costheta = dot(v1, v2) / lenlen;
  if (costheta > 1.0) {
    costheta = 1.0; // Roundoff. 
  } else if (costheta < -1.0) {
    costheta = -1.0; // Roundoff. 
  }
  theta = acos(costheta);
  if (n != NULL) {
    // Get a point above the face (o, p1, p2);
    np[0] = o[0] + n[0];
    np[1] = o[1] + n[1];
    np[2] = o[2] + n[2];
    // Adjust theta (0 - 2 * PI).
    ori = orient3d(p1, o, np, p2);
    if (ori > 0.0) {
      theta = 2 * PI - theta;
    }
  }

  return theta;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// projpt2edge()    Return the projection point from a point to an edge.     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::projpt2edge(REAL* p, REAL* e1, REAL* e2, REAL* prj)
{
  REAL v1[3], v2[3];
  REAL len, l_p;

  v1[0] = e2[0] - e1[0];
  v1[1] = e2[1] - e1[1];
  v1[2] = e2[2] - e1[2];
  v2[0] = p[0] - e1[0];
  v2[1] = p[1] - e1[1];
  v2[2] = p[2] - e1[2];

  len = sqrt(dot(v1, v1));
#ifdef SELF_CHECK
  //assert(len != 0.0);
#endif
  v1[0] /= len;
  v1[1] /= len;
  v1[2] /= len;
  l_p = dot(v1, v2);

  prj[0] = e1[0] + l_p * v1[0];
  prj[1] = e1[1] + l_p * v1[1];
  prj[2] = e1[2] + l_p * v1[2];
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// projpt2face()    Return the projection point from a point to a face.      //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::projpt2face(REAL* p, REAL* f1, REAL* f2, REAL* f3, REAL* prj)
{
  REAL fnormal[3], v1[3];
  REAL len, dist;

  // Get the unit face normal.
  // facenormal(f1, f2, f3, fnormal, &len);
  facenormal2(f1, f2, f3, fnormal, 1);
  len = sqrt(fnormal[0]*fnormal[0] + fnormal[1]*fnormal[1] + 
    fnormal[2]*fnormal[2]);
  fnormal[0] /= len;
  fnormal[1] /= len;
  fnormal[2] /= len;
  // Get the vector v1 = |p - f1|.
  v1[0] = p[0] - f1[0];
  v1[1] = p[1] - f1[1];
  v1[2] = p[2] - f1[2];
  // Get the project distance.
  dist = dot(fnormal, v1);
  
  // Get the project point.
  prj[0] = p[0] - dist * fnormal[0];
  prj[1] = p[1] - dist * fnormal[1];
  prj[2] = p[2] - dist * fnormal[2];
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// facenormal()    Calculate the normal of a face given by three points.     //
//                                                                           //
// In general, the face normal can be calculate by the cross product of any  //
// pair of the three edge vectors.  However, if the three points are nearly  //
// collinear, the rounding error may harm the result. To choose a good pair  //
// of vectors is helpful to reduce the error.                                //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::facenormal(REAL* pa, REAL* pb, REAL* pc, REAL* n, REAL* nlen)
{
  REAL v1[3], v2[3];

  v1[0] = pb[0] - pa[0];
  v1[1] = pb[1] - pa[1];
  v1[2] = pb[2] - pa[2];
  v2[0] = pc[0] - pa[0];
  v2[1] = pc[1] - pa[1];
  v2[2] = pc[2] - pa[2];

  cross(v1, v2, n);
  if (nlen != (REAL *) NULL) {
    *nlen = sqrt(dot(n, n));
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// facenormal()    Calculate the normal of the face.                         //
//                                                                           //
// The normal of the face abc can be calculated by the cross product of 2 of //
// its 3 edge vectors.  A better choice of two edge vectors will reduce the  //
// numerical error during the calculation.  Burdakov proved that the optimal //
// basis problem is equivalent to the minimum spanning tree problem with the //
// edge length be the functional, see Burdakov, "A greedy algorithm for the  //
// optimal basis problem", BIT 37:3 (1997), 591-599. If 'pivot' > 0, the two //
// short edges in abc are chosen for the calculation.                        //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::facenormal2(point pa, point pb, point pc, REAL *n, int pivot)
{
  REAL v1[3], v2[3], v3[3], *pv1, *pv2;
  REAL L1, L2, L3;

  v1[0] = pb[0] - pa[0];  // edge vector v1: a->b
  v1[1] = pb[1] - pa[1];
  v1[2] = pb[2] - pa[2];
  v2[0] = pa[0] - pc[0];  // edge vector v2: c->a
  v2[1] = pa[1] - pc[1];
  v2[2] = pa[2] - pc[2];

  // Default, normal is calculated by: v1 x (-v2) (see Fig. fnormal).
  if (pivot > 0) {
    // Choose edge vectors by Burdakov's algorithm.
    v3[0] = pc[0] - pb[0];  // edge vector v3: b->c
    v3[1] = pc[1] - pb[1];
    v3[2] = pc[2] - pb[2];
    L1 = DOT(v1, v1);
    L2 = DOT(v2, v2);
    L3 = DOT(v3, v3);
    // Sort the three edge lengths.
    if (L1 < L2) {
      if (L2 < L3) {
        pv1 = v1; pv2 = v2; // n = v1 x (-v2).
      } else {
        pv1 = v3; pv2 = v1; // n = v3 x (-v1).
      }
    } else {
      if (L1 < L3) {
        pv1 = v1; pv2 = v2; // n = v1 x (-v2).
      } else {
        pv1 = v2; pv2 = v3; // n = v2 x (-v3).
      }
    }
  } else {
    pv1 = v1; pv2 = v2; // n = v1 x (-v2).
  }

  // Calculate the face normal.
  CROSS(pv1, pv2, n);
  // Inverse the direction;
  n[0] = -n[0];
  n[1] = -n[1];
  n[2] = -n[2];
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// edgeorthonormal()    Return the unit normal of an edge in a given plane.  //
//                                                                           //
// The edge is from e1 to e2,  the plane is defined by given an additional   //
// point op, which is non-collinear with the edge.  In addition, the side of //
// the edge in which op lies defines the positive position of the normal.    //
//                                                                           //
// Let v1 be the unit vector from e1 to e2, v2 be the unit edge vector from  //
// e1 to op, fn be the unit face normal calculated by fn = v1 x v2. Then the //
// unit edge normal of e1e2 pointing to op is n = fn x v1.  Note, we should  //
// not change the position of fn and v1, otherwise, we get the edge normal   //
// pointing to the other side of op.                                         //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::edgeorthonormal(REAL* e1, REAL* e2, REAL* op, REAL* n)
{
  REAL v1[3], v2[3], fn[3];
  REAL len;

  // Get the edge vector v1.
  v1[0] = e2[0] - e1[0];
  v1[1] = e2[1] - e1[1];
  v1[2] = e2[2] - e1[2];
  // Get the edge vector v2.
  v2[0] = op[0] - e1[0];
  v2[1] = op[1] - e1[1];
  v2[2] = op[2] - e1[2];
  // Get the face normal fn = v1 x v2.
  cross(v1, v2, fn);
  // Get the edge normal n pointing to op. n = fn x v1.
  cross(fn, v1, n);
  // Normalize the vector.
  len = sqrt(dot(n, n));
  n[0] /= len;
  n[1] /= len;
  n[2] /= len;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// facedihedral()    Return the dihedral angle (in radian) between two       //
//                   adjoining faces.                                        //
//                                                                           //
// 'pa', 'pb' are the shared edge of these two faces, 'pc1', and 'pc2' are   //
// apexes of these two faces.  Return the angle (between 0 to 2*pi) between  //
// the normal of face (pa, pb, pc1) and normal of face (pa, pb, pc2).        //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

REAL tetgenmesh::facedihedral(REAL* pa, REAL* pb, REAL* pc1, REAL* pc2)
{
  REAL n1[3], n2[3];
  REAL n1len, n2len;
  REAL costheta, ori;
  REAL theta;

  facenormal(pa, pb, pc1, n1, &n1len);
  facenormal(pa, pb, pc2, n2, &n2len);
  costheta = dot(n1, n2) / (n1len * n2len);
  // Be careful rounding error!
  if (costheta > 1.0) {
    costheta = 1.0;
  } else if (costheta < -1.0) {
    costheta = -1.0;
  }
  theta = acos(costheta);
  ori = orient3d(pa, pb, pc1, pc2);
  if (ori > 0.0) {
    theta = 2 * PI - theta;
  }

  return theta;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// tetalldihedral()    Get all (six) dihedral angles of a tet.               //
//                                                                           //
// The tet is given by its four corners a, b, c, and d. If 'cosdd' is not    //
// NULL, it returns the cosines of the 6 dihedral angles, the corresponding  //
// edges are: ab, bc, ca, ad, bd, and cd. If 'cosmaxd' (or 'cosmind') is not //
// NULL, it returns the cosine of the maximal (or minimal) dihedral angle.   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::tetalldihedral(point pa, point pb, point pc, point pd,
  REAL* cosdd, REAL* cosmaxd, REAL* cosmind)
{
  REAL N[4][3], vol, cosd, len;
  int f1, f2, i, j;

  vol = 0; // Check if the tet is valid or not.

  // Get four normals of faces of the tet.
  tetallnormal(pa, pb, pc, pd, N, &vol);

  if (vol == 0.0) {
    // This tet is not valid.
    if (cosdd != NULL) {
      for (i = 0; i < 6; i++) {
        cosdd[i] = -1.0; // 180 degree.
      }
    }
    // This tet has zero volume.
    if (cosmaxd != NULL) {
      *cosmaxd = -1.0; // 180 degree.
    }
    if (cosmind != NULL) {
      *cosmind = 1.0; // 0 degree.
    }
    return;
  }

  // Normalize the normals.
  for (i = 0; i < 4; i++) {
    len = sqrt(dot(N[i], N[i]));
    if (len != 0.0) {
      for (j = 0; j < 3; j++) N[i][j] /= len;
    }
  }

  for (i = 0; i < 6; i++) {
    switch (i) {
    case 0: f1 = 2; f2 = 3; break; // edge ab.
    case 1: f1 = 0; f2 = 3; break; // edge bc.
    case 2: f1 = 1; f2 = 3; break; // edge ca.
    case 3: f1 = 1; f2 = 2; break; // edge ad.
    case 4: f1 = 2; f2 = 0; break; // edge bd.
    case 5: f1 = 0; f2 = 1; break; // edge cd.
    }
    cosd = -dot(N[f1], N[f2]);
    if (cosdd) cosdd[i] = cosd;
    if (i == 0) {
      if (cosmaxd) *cosmaxd = cosd;
      if (cosmind) *cosmind = cosd;
    } else {
      if (cosmaxd) *cosmaxd = cosd < *cosmaxd ? cosd : *cosmaxd;
      if (cosmind) *cosmind = cosd > *cosmind ? cosd : *cosmind;
    }
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// tetallnormal()    Get the in-noramls of the four faces of a given tet.    //
//                                                                           //
// Let tet be abcd. N[4][3] returns the four normals, which are: N[0] cbd,   //
// N[1] acd, N[2] bad, N[3] abc. These normals are unnormalized.             //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::tetallnormal(point pa, point pb, point pc, point pd,
  REAL N[4][3], REAL* volume)
{
  REAL A[4][4], rhs[4], D;
  int indx[4];
  int i, j;

  // get the entries of A[3][3].
  for (i = 0; i < 3; i++) A[0][i] = pa[i] - pd[i];  // d->a vec
  for (i = 0; i < 3; i++) A[1][i] = pb[i] - pd[i];  // d->b vec
  for (i = 0; i < 3; i++) A[2][i] = pc[i] - pd[i];  // d->c vec
  // Compute the inverse of matrix A, to get 3 normals of the 4 faces.
  lu_decmp(A, 3, indx, &D, 0);     // Decompose the matrix just once.
  if (volume != NULL) {
    // Get the volume of the tet.
    *volume = fabs((A[indx[0]][0] * A[indx[1]][1] * A[indx[2]][2])) / 6.0;
  }
  for (j = 0; j < 3; j++) {
    for (i = 0; i < 3; i++) rhs[i] = 0.0;
    rhs[j] = 1.0;  // Positive means the inside direction
    lu_solve(A, 3, indx, rhs, 0);
    for (i = 0; i < 3; i++) N[j][i] = rhs[i];
  }
  // Get the fourth normal by summing up the first three.
  for (i = 0; i < 3; i++) N[3][i] = - N[0][i] - N[1][i] - N[2][i];
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// tetaspectratio()    Calculate the aspect ratio of the tetrahedron.        //
//                                                                           //
// The aspect ratio of a tet is R/h, where R is the circumradius and h is    //
// the shortest height of the tet.                                           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

REAL tetgenmesh::tetaspectratio(point pa, point pb, point pc, point pd)
{
  REAL vda[3], vdb[3], vdc[3];
  REAL N[4][3], A[4][4], rhs[4], D;
  REAL H[4], volume, radius2, minheightinv;
  int indx[4];
  int i, j; 

  // Set the matrix A = [vda, vdb, vdc]^T.
  for (i = 0; i < 3; i++) A[0][i] = vda[i] = pa[i] - pd[i];
  for (i = 0; i < 3; i++) A[1][i] = vdb[i] = pb[i] - pd[i];
  for (i = 0; i < 3; i++) A[2][i] = vdc[i] = pc[i] - pd[i];
  // Lu-decompose the matrix A.
  lu_decmp(A, 3, indx, &D, 0);
  // Get the volume of abcd.
  volume = (A[indx[0]][0] * A[indx[1]][1] * A[indx[2]][2]) / 6.0;
  // Check if it is zero.
  if (volume == 0.0) return 1.0e+200; // A degenerate tet.
  // if (volume < 0.0) volume = -volume;
  // Check the radiu-edge ratio of the tet.
  rhs[0] = 0.5 * dot(vda, vda);
  rhs[1] = 0.5 * dot(vdb, vdb);
  rhs[2] = 0.5 * dot(vdc, vdc);
  lu_solve(A, 3, indx, rhs, 0);
  // Get the circumcenter.
  // for (i = 0; i < 3; i++) circumcent[i] = pd[i] + rhs[i];
  // Get the square of the circumradius.
  radius2 = dot(rhs, rhs);

  // Compute the 4 face normals (N[0], ..., N[3]).
  for (j = 0; j < 3; j++) {
    for (i = 0; i < 3; i++) rhs[i] = 0.0;
    rhs[j] = 1.0;  // Positive means the inside direction
    lu_solve(A, 3, indx, rhs, 0);
    for (i = 0; i < 3; i++) N[j][i] = rhs[i];
  }
  // Get the fourth normal by summing up the first three.
  for (i = 0; i < 3; i++) N[3][i] = - N[0][i] - N[1][i] - N[2][i];
  // Normalized the normals.
  for (i = 0; i < 4; i++) {
    // H[i] is the inverse of the height of its corresponding face.
    H[i] = sqrt(dot(N[i], N[i]));
    // if (H[i] > 0.0) {
    //   for (j = 0; j < 3; j++) N[i][j] /= H[i];
    // }
  }
  // Get the radius of the inscribed sphere.
  // insradius = 1.0 / (H[0] + H[1] + H[2] + H[3]);
  // Get the biggest H[i] (corresponding to the smallest height).
  minheightinv = H[0];
  for (i = 1; i < 3; i++) {
    if (H[i] > minheightinv) minheightinv = H[i];
  }

  return sqrt(radius2) * minheightinv;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// circumsphere()    Calculate the smallest circumsphere (center and radius) //
//                   of the given three or four points.                      //
//                                                                           //
// The circumsphere of four points (a tetrahedron) is unique if they are not //
// degenerate. If 'pd = NULL', the smallest circumsphere of three points is  //
// the diametral sphere of the triangle if they are not degenerate.          //
//                                                                           //
// Return TRUE if the input points are not degenerate and the circumcenter   //
// and circumradius are returned in 'cent' and 'radius' respectively if they //
// are not NULLs. Otherwise, return FALSE indicated the points are degenrate.//
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::
circumsphere(REAL* pa, REAL* pb, REAL* pc, REAL* pd, REAL* cent, REAL* radius)
{
  REAL A[4][4], rhs[4], D;
  int indx[4];

  // Compute the coefficient matrix A (3x3).
  A[0][0] = pb[0] - pa[0];
  A[0][1] = pb[1] - pa[1];
  A[0][2] = pb[2] - pa[2];
  A[1][0] = pc[0] - pa[0];
  A[1][1] = pc[1] - pa[1];
  A[1][2] = pc[2] - pa[2];
  if (pd != NULL) {
    A[2][0] = pd[0] - pa[0];
    A[2][1] = pd[1] - pa[1]; 
    A[2][2] = pd[2] - pa[2];
  } else {
    cross(A[0], A[1], A[2]);
  }

  // Compute the right hand side vector b (3x1).
  rhs[0] = 0.5 * dot(A[0], A[0]);
  rhs[1] = 0.5 * dot(A[1], A[1]);
  if (pd != NULL) {
    rhs[2] = 0.5 * dot(A[2], A[2]);
  } else {
    rhs[2] = 0.0;
  }

  // Solve the 3 by 3 equations use LU decomposition with partial pivoting
  //   and backward and forward substitute..
  if (!lu_decmp(A, 3, indx, &D, 0)) {
    if (radius != (REAL *) NULL) *radius = 0.0;
    return false;
  }    
  lu_solve(A, 3, indx, rhs, 0);
  if (cent != (REAL *) NULL) {
    cent[0] = pa[0] + rhs[0];
    cent[1] = pa[1] + rhs[1];
    cent[2] = pa[2] + rhs[2];
  }
  if (radius != (REAL *) NULL) {
    *radius = sqrt(rhs[0] * rhs[0] + rhs[1] * rhs[1] + rhs[2] * rhs[2]);
  }
  return true;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// inscribedsphere()    Compute the radius and center of the biggest         //
//                      inscribed sphere of a given tetrahedron.             //
//                                                                           //
// The tetrahedron is given by its four points, it must not be degenerate.   //
// The center and radius are returned in 'cent' and 'radius' respectively if //
// they are not NULLs.                                                       //
//                                                                           //
// Geometrical fact. For any simplex in d dimension,                         //
//   r/h1 + r/h2 + ... r/hn = 1 (n <= d + 1);                                //
// where r is the radius of inscribed ball, and h is the height of each side //
// of the simplex. The value of 'r/h' is just the barycenter coordinates of  //
// each vertex of the simplex. Therefore, we can compute the radius and      //
// center of the smallest inscribed ball as following equations:             //
//   r = 1.0 / (1/h1 + 1/h2 + ... + 1/hn);          (1)                      //
//   C = r/h1 * P1 + r/h2 * P2 + ... + r/hn * Pn;   (2)                      //
// where C is the vector of center, P1, P2, .. Pn are vectors of vertices.   //
// Here (2) contains n linear equations with n variables.  (h, P) must be a  //
// pair, h is the height from P to its opposite face.                        //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::inscribedsphere(REAL* pa, REAL* pb, REAL* pc, REAL* pd,
  REAL* cent, REAL* radius)
{
  REAL N[4][3], H[4]; // Normals (colume vectors) and heights of each face.
  REAL rd;
  int i;  

  // Get the all normals of the tet.
  tetallnormal(pa, pb, pc, pd, N, NULL);
  for (i = 0; i < 4; i++) {
    // H[i] is the inverse of height of its corresponding face.
    H[i] = sqrt(dot(N[i], N[i]));
  }
  // Compute the radius use eq. (1).
  rd = 1.0 / (H[0] + H[1] + H[2] + H[3]);
  if (radius != (REAL*) NULL) *radius = rd;
  if (cent != (REAL*) NULL) {
    // Compute the center use eq. (2).
    cent[0] = rd * (H[0] * pa[0] + H[1] * pb[0] + H[2] * pc[0] + H[3] * pd[0]);
    cent[1] = rd * (H[0] * pa[1] + H[1] * pb[1] + H[2] * pc[1] + H[3] * pd[1]);
    cent[2] = rd * (H[0] * pa[2] + H[1] * pb[2] + H[2] * pc[2] + H[3] * pd[2]);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// rotatepoint()    Create a point by rotating an existing point.            //
//                                                                           //
// Create a 3D point by rotating point 'p' with an angle 'rotangle' (in arc  //
// degree) around a rotating axis given by a vector from point 'p1' to 'p2'. //
// The rotation is according with right-hand rule, i.e., use your right-hand //
// to grab the axis with your thumber pointing to its positive direction,    //
// your fingers indicate the rotating direction.                             //
//                                                                           //
// The rotating steps are the following:                                     //
//   1. Translate vector 'p1->p2' to origin, M1;                             //
//   2. Rotate vector around the Y-axis until it lies in the YZ plane, M2;   //
//   3. Rotate vector around the X-axis until it lies on the Z axis, M3;     //
//   4. Perform the rotation of 'p' around the z-axis, M4;                   //
//   5. Undo Step 3, M5;                                                     //
//   6. Undo Step 2, M6;                                                     //
//   7. Undo Step 1, M7;                                                     //
// Use matrix multiplication to combine the above sequences, we get:         //
//   p0' = T * p0, where T = M7 * M6 * M5 * M4 * M3 * M2 * M1                //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::rotatepoint(REAL* p, REAL rotangle, REAL* p1, REAL* p2)
{
  REAL T[4][4], pp0[4], p0t[4], p2t[4];
  REAL roty, rotx, alphaR, projlen;
  REAL dx, dy, dz;

  initm44(1, 0, 0, -p1[0],
          0, 1, 0, -p1[1],
          0, 0, 1, -p1[2],
          0, 0, 0, 1, T);
  pp0[0] = p[0]; pp0[1] = p[1]; pp0[2] = p[2]; pp0[3] = 1.0;
  m4xv4(p0t, T, pp0); // Step 1
  pp0[0] = p2[0]; pp0[1] = p2[1]; pp0[2] = p2[2]; pp0[3] = 1.0;
  m4xv4(p2t, T, pp0); // Step 1

  // Get the rotation angle around y-axis;
  dx = p2t[0];
  dz = p2t[2];
  projlen = sqrt(dx * dx + dz * dz);
  if (projlen <= (b->epsilon * 1e-2) * longest) {
    roty = 0;
  } else {
    roty = acos(dz / projlen);
    if (dx < 0) {
      roty = -roty;
    }
  }

  initm44(cos(-roty), 0, sin(-roty), 0,
          0, 1, 0, 0,
          -sin(-roty), 0, cos(-roty), 0,
          0, 0, 0, 1, T);
  pp0[0] = p0t[0]; pp0[1] = p0t[1]; pp0[2] = p0t[2]; pp0[3] = 1.0;
  m4xv4(p0t, T, pp0); // Step 2
  pp0[0] = p2t[0]; pp0[1] = p2t[1]; pp0[2] = p2t[2]; pp0[3] = 1.0;
  m4xv4(p2t, T, pp0); // Step 2

  // Get the rotation angle around x-axis
  dy = p2t[1];
  dz = p2t[2];
  projlen = sqrt(dy * dy + dz * dz);
  if (projlen <= (b->epsilon * 1e-2) * longest) {
    rotx = 0;
  } else {
    rotx = acos(dz / projlen);
    if (dy < 0) {
      rotx = -rotx;
    }
  }
    
  initm44(1, 0, 0, 0,
          0, cos(rotx), -sin(rotx), 0,
          0, sin(rotx), cos(rotx), 0,
          0, 0, 0, 1, T);
  pp0[0] = p0t[0]; pp0[1] = p0t[1]; pp0[2] = p0t[2]; pp0[3] = 1.0;
  m4xv4(p0t, T, pp0); // Step 3
  // pp0[0] = p2t[0]; pp0[1] = p2t[1]; pp0[2] = p2t[2]; pp0[3] = 1.0;
  // m4xv4(p2t, T, pp0); // Step 3

  alphaR = rotangle;
  initm44(cos(alphaR), -sin(alphaR), 0, 0,
          sin(alphaR), cos(alphaR), 0, 0,
          0, 0, 1, 0,
          0, 0, 0, 1, T);
  pp0[0] = p0t[0]; pp0[1] = p0t[1]; pp0[2] = p0t[2]; pp0[3] = 1.0;
  m4xv4(p0t, T, pp0); // Step 4

  initm44(1, 0, 0, 0,
          0, cos(-rotx), -sin(-rotx), 0,
          0, sin(-rotx), cos(-rotx), 0,
          0, 0, 0, 1, T);
  pp0[0] = p0t[0]; pp0[1] = p0t[1]; pp0[2] = p0t[2]; pp0[3] = 1.0;
  m4xv4(p0t, T, pp0); // Step 5

  initm44(cos(roty), 0, sin(roty), 0,
          0, 1, 0, 0,
          -sin(roty), 0, cos(roty), 0,
          0, 0, 0, 1, T);
  pp0[0] = p0t[0]; pp0[1] = p0t[1]; pp0[2] = p0t[2]; pp0[3] = 1.0;
  m4xv4(p0t, T, pp0); // Step 6

  initm44(1, 0, 0, p1[0],
          0, 1, 0, p1[1],
          0, 0, 1, p1[2],
          0, 0, 0, 1, T);
  pp0[0] = p0t[0]; pp0[1] = p0t[1]; pp0[2] = p0t[2]; pp0[3] = 1.0;
  m4xv4(p0t, T, pp0); // Step 7  

  p[0] = p0t[0];
  p[1] = p0t[1];
  p[2] = p0t[2];
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// planelineint()    Calculate the intersection of a line and a plane.       //
//                                                                           //
// The equation of a plane (points P are on the plane with normal N and P3   //
// on the plane) can be written as: N dot (P - P3) = 0. The equation of the  //
// line (points P on the line passing through P1 and P2) can be written as:  //
// P = P1 + u (P2 - P1). The intersection of these two occurs when:          //
//   N dot (P1 + u (P2 - P1)) = N dot P3.                                    //
// Solving for u gives:                                                      //
//         N dot (P3 - P1)                                                   //
//   u = ------------------.                                                 //
//         N dot (P2 - P1)                                                   //
// If the denominator is 0 then N (the normal to the plane) is perpendicular //
// to the line.  Thus the line is either parallel to the plane and there are //
// no solutions or the line is on the plane in which case there are an infi- //
// nite number of solutions.                                                 //
//                                                                           //
// The plane is given by three points pa, pb, and pc, e1 and e2 defines the  //
// line. If u is non-zero, The intersection point (if exists) returns in ip. //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::planelineint(REAL* pa, REAL* pb, REAL* pc, REAL* e1, REAL* e2,
  REAL* ip, REAL* u)
{
  REAL n[3], det, det1;

  // Calculate N.
  facenormal2(pa, pb, pc, n, 1);
  // Calculate N dot (e2 - e1).
  det = n[0] * (e2[0] - e1[0]) + n[1] * (e2[1] - e1[1])
      + n[2] * (e2[2] - e1[2]);
  if (det != 0.0) {
    // Calculate N dot (pa - e1)
    det1 = n[0] * (pa[0] - e1[0]) + n[1] * (pa[1] - e1[1])
         + n[2] * (pa[2] - e1[2]);
    *u = det1 / det;
    ip[0] = e1[0] + *u * (e2[0] - e1[0]);
    ip[1] = e1[1] + *u * (e2[1] - e1[1]);
    ip[2] = e1[2] + *u * (e2[2] - e1[2]);
  } else {
    *u = 0.0;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// randomnation()    Generate a random number between 0 and 'choices' - 1.   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

unsigned long tetgenmesh::randomnation(unsigned int choices)
{
  unsigned long newrandom;

  if (choices >= 714025l) {
    newrandom = (randomseed * 1366l + 150889l) % 714025l;
    randomseed = (newrandom * 1366l + 150889l) % 714025l;
    newrandom = newrandom * (choices / 714025l) + randomseed;
    if (newrandom >= choices) {
      return newrandom - choices;
    } else {
      return newrandom;
    }
  } else {
    randomseed = (randomseed * 1366l + 150889l) % 714025l;
    return randomseed % choices;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// distance2()    Returns the square "distance" of a tetrahedron to point p. //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

REAL tetgenmesh::distance2(tetrahedron* tetptr, point p)
{
  point p1, p2, p3, p4;
  REAL dx, dy, dz;

  p1 = (point) tetptr[4];
  p2 = (point) tetptr[5];
  p3 = (point) tetptr[6];
  p4 = (point) tetptr[7];

  dx = p[0] - 0.25 * (p1[0] + p2[0] + p3[0] + p4[0]);
  dy = p[1] - 0.25 * (p1[1] + p2[1] + p3[1] + p4[1]);
  dz = p[2] - 0.25 * (p1[2] + p2[2] + p3[2] + p4[2]);

  return dx * dx + dy * dy + dz * dz;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// preciselocate()    Find a simplex containing a given point.               //
//                                                                           //
// This routine implements the simple Walk-through point location algorithm. //
// Begins its search from 'searchtet', assume there is a line segment L from //
// a vertex of 'searchtet' to the query point 'searchpt', and simply walk    //
// towards 'searchpt' by traversing all faces intersected by L.              //
//                                                                           //
// On completion, 'searchtet' is a tetrahedron that contains 'searchpt'. The //
// returned value indicates one of the following cases:                      //
//   - ONVERTEX, the search point lies on the origin of 'searchtet'.         //
//   - ONEDGE, the search point lies on an edge of 'searchtet'.              //
//   - ONFACE, the search point lies on a face of 'searchtet'.               //
//   - INTET, the search point lies in the interior of 'searchtet'.          //
//   - OUTSIDE, the search point lies outside the mesh. 'searchtet' is a     //
//     hull tetrahedron whose base face is visible by the search point.      //
//                                                                           //
// WARNING: This routine is designed for convex triangulations, and will not //
// generally work after the holes and concavities have been carved.          //
//                                                                           //
// If 'maxtetnumber' > 0, stop the searching process if the number of passed //
// tets is larger than it and return OUTSIDE.                                //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

enum tetgenmesh::locateresult tetgenmesh::preciselocate(point searchpt,
  triface* searchtet, long maxtetnumber)
{
  triface backtracetet;
  triface walkthroface;
  point forg, fdest, fapex, toppo;
  REAL ori1, ori2, ori3, ori4;
  long tetnumber;
  int side;

  if (isdead(searchtet)) searchtet->tet = dummytet;
  if (searchtet->tet == dummytet) {
    searchtet->loc = 0;
    symself(*searchtet);
  }
  // 'searchtet' should be a valid tetrahedron now.
#ifdef SELF_CHECK
  //assert(searchtet->tet != dummytet);
#endif

  searchtet->ver = 0; // Keep in CCW edge ring.
  // Find a face of 'searchtet' such that the 'searchpt' lies strictly
  //   above it.  Such face should always exist.
  for (searchtet->loc = 0; searchtet->loc < 4; searchtet->loc++) {
    forg = org(*searchtet);
    fdest = dest(*searchtet);
    fapex = apex(*searchtet);
    ori1 = orient3d(forg, fdest, fapex, searchpt);
    if (ori1 < 0.0) break;
  }
#ifdef SELF_CHECK
  //assert(searchtet->loc < 4);
#endif

  backtracetet = *searchtet; // Initialize backtracetet.

  // Define 'tetnumber' for exit the loop when it's running endless.
  tetnumber = 0l;
  while ((maxtetnumber > 0l) && (tetnumber <= maxtetnumber)) {
    ptloc_count++;  // Algorithimic count.
    // Check if we are reaching the boundary of the triangulation.
    if (searchtet->tet == dummytet) {
      *searchtet = backtracetet;
      return OUTSIDE;
    }
    // Initialize the face for returning the walk-through face.
    walkthroface.tet = (tetrahedron *) NULL;
    // Adjust the edge ring, so that 'ori1 < 0.0' holds.
    searchtet->ver = 0;
    // 'toppo' remains unchange for the following orientation tests.
    toppo = oppo(*searchtet);
    // Check the three sides of 'searchtet' to find the face through which
    //   we can walk next.
    for (side = 0; side < 3; side++) {
      forg = org(*searchtet);
      fdest = dest(*searchtet);
      ori2 = orient3d(forg, fdest, toppo, searchpt);
      if (ori2 == 0.0) {
        // They are coplanar, check if 'searchpt' lies inside, or on an edge,
        //   or coindice with a vertex of face (forg, fdest, toppo). 
        fapex = apex(*searchtet);
        ori3 = orient3d(fdest, fapex, toppo, searchpt);
        if (ori3 < 0.0) {
          // Outside the face (fdest, fapex, toppo), walk through it.
          enextself(*searchtet);
          fnext(*searchtet, walkthroface);
          break;
        }
        ori4 = orient3d(fapex, forg, toppo, searchpt);
        if (ori4 < 0.0) {
          // Outside the face (fapex, forg, toppo), walk through it.
          enext2self(*searchtet);
          fnext(*searchtet, walkthroface);
          break;
        }
        // Remember, ori1 < 0.0, which means that 'searchpt' will not on edge
        //   (forg, fdest) or on vertex forg or fdest.
        // The rest possible cases are: 
        //   (1) 'searchpt' lies on edge (fdest, toppo);
        //   (2) 'searchpt' lies on edge (toppo, forg);
        //   (3) 'searchpt' coincident with toppo;
        //   (4) 'searchpt' lies inside face (forg, fdest, toppo).
        fnextself(*searchtet);
        if (ori3 == 0.0) {
          if (ori4 == 0.0) {
            // Case (4).
            enext2self(*searchtet);
            return ONVERTEX;
          } else {
            // Case (1).
            enextself(*searchtet);
            return ONEDGE;
          }
        }
        if (ori4 == 0.0) {
          // Case (2).
          enext2self(*searchtet);
          return ONEDGE;
        }
        // Case (4).
        return ONFACE;
      } else if (ori2 < 0.0) {
        // Outside the face (forg, fdest, toppo), walk through it.
        fnext(*searchtet, walkthroface);
        break;
      }
      // Go to check next side.
      enextself(*searchtet);
    }
    if (side == 3) {
      // Found! Inside tetrahedron.
      return INTETRAHEDRON;
    }
    // We walk through the face 'walkthroface' and continue the searching.
    // Store the face handle in 'backtracetet' before we take the real walk.
    //   So we are able to restore the handle to 'searchtet' if we are
    //   reaching the outer boundary.
    backtracetet = walkthroface;
    sym(walkthroface, *searchtet);    
    tetnumber++;
  }

  if (!b->quiet && b->verbose) {
    //printf("Warning:  Point location stopped after searching %ld tets.\n",
      //maxtetnumber);
  }
  // terminatetetgen(2);
  return OUTSIDE;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// randomsample()    Randomly sample the tetrahedra for point loation.       //
//                                                                           //
// This routine implements Muecke's Jump-and-walk point location algorithm.  //
// It improves the simple walk-through by "jumping" to a good starting point //
// via random sampling.  Searching begins from one of handles:  the input    //
// 'searchtet', a recently encountered tetrahedron 'recenttet',  or from one //
// chosen from a random sample.  The choice is made by determining which one //
// 's origin is closest to the point we are searcing for.  Having chosen the //
// starting tetrahedron, the simple Walk-through algorithm is executed.      //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::randomsample(point searchpt, triface *searchtet)
{
  tetrahedron *firsttet, *tetptr;
  void **sampleblock;
  long sampleblocks, samplesperblock, samplenum;
  long tetblocks, i, j;
  uintptr_t alignptr;
  REAL searchdist, dist;

  // 'searchtet' should be a valid tetrahedron.
  if (isdead(searchtet)) {
    searchtet->tet = dummytet;
  }
  if (searchtet->tet == dummytet) {
    // This is an 'Outer Space' handle, get a hull tetrahedron.
    searchtet->loc = 0;
    symself(*searchtet);
  }

  // Note 'searchtet' may be dead (chnaged in constrainedcavity2()).
  if (!isdead(searchtet)) {
    // Get the distance from the suggested starting tet to the point we seek.
    searchdist = distance2(searchtet->tet, searchpt);
  } else {
    searchdist = longest * longest;
  }

  // If a recently encountered tetrahedron has been recorded and has not
  //   been deallocated, test it as a good starting point.
  if (!isdead(&recenttet) && (recenttet.tet != searchtet->tet)) {
    dist = distance2(recenttet.tet, searchpt);
    if (dist < searchdist) {
      *searchtet = recenttet;
      searchdist = dist;
    }
  }

  // Select "good" candidate using k random samples, taking the closest one.
  //   The number of random samples taken is proportional to the fourth root
  //   of the number of tetrahedra in the mesh. The next bit of code assumes
  //   that the number of tetrahedra increases monotonically.
  while (SAMPLEFACTOR * samples * samples * samples * samples <
         tetrahedrons->items) {
    samples++;
  }
  // Find how much blocks in current tet pool.
  tetblocks = (tetrahedrons->maxitems + ELEPERBLOCK - 1) / ELEPERBLOCK;
  // Find the average samles per block. Each block at least have 1 sample.
  samplesperblock = 1 + (samples / tetblocks);
  sampleblocks = samples / samplesperblock;
  sampleblock = tetrahedrons->firstblock;
  for (i = 0; i < sampleblocks; i++) {
    alignptr = (uintptr_t) (sampleblock + 1);
    firsttet = (tetrahedron *)
               (alignptr + (uintptr_t) tetrahedrons->alignbytes
               - (alignptr % (uintptr_t) tetrahedrons->alignbytes));
    for (j = 0; j < samplesperblock; j++) {
      if (i == tetblocks - 1) {
        // This is the last block.
        samplenum = randomnation((int)
                      (tetrahedrons->maxitems - (i * ELEPERBLOCK)));
      } else {
        samplenum = randomnation(ELEPERBLOCK);
      }
      tetptr = (tetrahedron *)
               (firsttet + (samplenum * tetrahedrons->itemwords));
      if (tetptr[4] != (tetrahedron) NULL) {
        dist = distance2(tetptr, searchpt);
        if (dist < searchdist) {
          searchtet->tet = tetptr;
          searchdist = dist;
        }
      }
    }
    sampleblock = (void **) *sampleblock;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// locate()    Find a simplex containing a given point.                      //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

enum tetgenmesh::locateresult tetgenmesh::locate(point searchpt, 
  triface *searchtet)
{
  // Randomly sample for a good starting tet.
  randomsample(searchpt, searchtet);
  // Call simple walk-through to locate the point.
  return preciselocate(searchpt, searchtet, tetrahedrons->items); 
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// locate2()    Find a simplex containing a given point.                     //
//                                                                           //
// Another implementation of the Walk-through point location algorithm.      //
// See the comments of preciselocate().                                      //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

enum tetgenmesh::locateresult tetgenmesh::locate2(point searchpt,
  triface* searchtet, arraypool *histtetarray)
{
  triface neightet, backtracetet, *parytet;
  point torg, tdest, tapex, toppo, ntoppo;
  enum {ORGMOVE, DESTMOVE, APEXMOVE} nextmove;
  REAL ori, oriorg, oridest, oriapex;
  REAL searchdist, dist;
  enum locateresult loc;
  int i;

  if (searchtet->tet == dummytet) {
    // A hull tet. Choose the neighbor of its base face.
    searchtet->loc = 0;
    symself(*searchtet);
  }

  // Stay in the 0th edge ring.
  searchtet->ver = 0;

  // Let searchtet be the face such that 'searchpt' lies above to it.
  for (searchtet->loc = 0; searchtet->loc < 4; searchtet->loc++) {
    torg = org(*searchtet);
    tdest = dest(*searchtet);
    tapex = apex(*searchtet);
    ori = orient3d(torg, tdest, tapex, searchpt); orient3dcount++;
    if (ori < 0.0) break;
  }
  if (!(searchtet->loc < 4)) {
    // Either 'searchtet' is a very flat tet, or the 'searchpt' lies in
    //   infinity, or both of them. Return OUTSIDE.
    return OUTSIDE;
  }

  if (histtetarray != NULL) {
    // Remember all the tets we've visited.
    //assert(histtetarray->objects == 0l);
    infect(*searchtet);
    histtetarray->newindex((void **) &parytet);
    *parytet = *searchtet;
  }

  loc = OUTSIDE; // Set a default return value.

  // Walk through tetrahedra to locate the point.
  while (true) {

    ptloc_count++;  // Algorithimic count.

    toppo = oppo(*searchtet);
    
    // Check if the vertex is we seek.
    if (toppo == searchpt) {
      // Adjust the origin of searchtet to be searchpt.
      fnextself(*searchtet);
      esymself(*searchtet);
      enext2self(*searchtet);
      loc = ONVERTEX; // return ONVERTEX;
      break;
    }

    // We enter from serarchtet's base face. There are three other faces in
    //   searchtet (all connecting to toppo), which one is the exit?
    oriorg = orient3d(tdest, tapex, toppo, searchpt); 
    oridest = orient3d(tapex, torg, toppo, searchpt);
    oriapex = orient3d(torg, tdest, toppo, searchpt);
    orient3dcount+=3;

    // Now decide which face to move. It is possible there are more than one
    //   faces are viable moves. Use the opposite points of thier neighbors
    //   to discriminate, i.e., we choose the face whose opposite point has
    //   the shortest distance to searchpt.
    if (oriorg < 0) {
      if (oridest < 0) {
        if (oriapex < 0) {
          // Any of the three faces is a viable move. 
          nextmove = ORGMOVE;
          enextfnext(*searchtet, neightet);
          symself(neightet);
          if (neightet.tet != dummytet) {
            ntoppo = oppo(neightet);
            searchdist = NORM2(searchpt[0] - ntoppo[0],
                               searchpt[1] - ntoppo[1],
                               searchpt[2] - ntoppo[2]);
          } else {
            searchdist = NORM2(xmax - xmin, ymax - ymin, zmax - zmin);
          }
          enext2fnext(*searchtet, neightet);
          symself(neightet);
          if (neightet.tet != dummytet) {
            ntoppo = oppo(neightet);
            dist = NORM2(searchpt[0] - ntoppo[0], searchpt[1] - ntoppo[1],
                         searchpt[2] - ntoppo[2]);
          } else {
            dist = searchdist;
          }
          if (dist < searchdist) {
            nextmove = DESTMOVE;
            searchdist = dist;
          }
          fnext(*searchtet, neightet);
          symself(neightet);
          if (neightet.tet != dummytet) {
            ntoppo = oppo(neightet);
            dist = NORM2(searchpt[0] - ntoppo[0], searchpt[1] - ntoppo[1],
                         searchpt[2] - ntoppo[2]);
          } else {
            dist = searchdist;
          }
          if (dist < searchdist) {
            nextmove = APEXMOVE;
            searchdist = dist;
          }
        } else {
          // Two faces, opposite to origin and destination, are viable.
          nextmove = ORGMOVE;
          enextfnext(*searchtet, neightet);
          symself(neightet);
          if (neightet.tet != dummytet) {
            ntoppo = oppo(neightet);
            searchdist = NORM2(searchpt[0] - ntoppo[0],
                               searchpt[1] - ntoppo[1],
                               searchpt[2] - ntoppo[2]);
          } else {
            searchdist = NORM2(xmax - xmin, ymax - ymin, zmax - zmin);
          }
          enext2fnext(*searchtet, neightet);
          symself(neightet);
          if (neightet.tet != dummytet) {
            ntoppo = oppo(neightet);
            dist = NORM2(searchpt[0] - ntoppo[0], searchpt[1] - ntoppo[1],
                         searchpt[2] - ntoppo[2]);
          } else {
            dist = searchdist;
          }
          if (dist < searchdist) {
            nextmove = DESTMOVE;
            searchdist = dist;
          }
        }
      } else {
        if (oriapex < 0) {
          // Two faces, opposite to origin and apex, are viable.
          nextmove = ORGMOVE;
          enextfnext(*searchtet, neightet);
          symself(neightet);
          if (neightet.tet != dummytet) {
            ntoppo = oppo(neightet);
            searchdist = NORM2(searchpt[0] - ntoppo[0],
                               searchpt[1] - ntoppo[1],
                               searchpt[2] - ntoppo[2]);
          } else {
            searchdist = NORM2(xmax - xmin, ymax - ymin, zmax - zmin);
          }
          fnext(*searchtet, neightet);
          symself(neightet);
          if (neightet.tet != dummytet) {
            ntoppo = oppo(neightet);
            dist = NORM2(searchpt[0] - ntoppo[0], searchpt[1] - ntoppo[1],
                         searchpt[2] - ntoppo[2]);
          } else {
            dist = searchdist;
          }
          if (dist < searchdist) {
            nextmove = APEXMOVE;
            searchdist = dist;
          }
        } else {
          // Only the face opposite to origin is viable.
          nextmove = ORGMOVE;
        }
      }
    } else {
      if (oridest < 0) {
        if (oriapex < 0) {
          // Two faces, opposite to destination and apex, are viable.
          nextmove = DESTMOVE;
          enext2fnext(*searchtet, neightet);
          symself(neightet);
          if (neightet.tet != dummytet) {
            ntoppo = oppo(neightet);
            searchdist = NORM2(searchpt[0] - ntoppo[0],
                               searchpt[1] - ntoppo[1],
                               searchpt[2] - ntoppo[2]);
          } else {
            searchdist = NORM2(xmax - xmin, ymax - ymin, zmax - zmin);
          }
          fnext(*searchtet, neightet);
          symself(neightet);
          if (neightet.tet != dummytet) {
            ntoppo = oppo(neightet);
            dist = NORM2(searchpt[0] - ntoppo[0], searchpt[1] - ntoppo[1],
                         searchpt[2] - ntoppo[2]);
          } else {
            dist = searchdist;
          }
          if (dist < searchdist) {
            nextmove = APEXMOVE;
            searchdist = dist;
          }
        } else {
          // Only the face opposite to destination is viable.
          nextmove = DESTMOVE;
        }
      } else {
        if (oriapex < 0) {
          // Only the face opposite to apex is viable.
          nextmove = APEXMOVE;
        } else {
          // The point we seek must be on the boundary of or inside this
          //   tetrahedron. Check for boundary cases.
          if (oriorg == 0) {
            // Go to the face opposite to origin.
            enextfnextself(*searchtet);
            if (oridest == 0) {
              enextself(*searchtet); // edge apex->oppo
              if (oriapex == 0) {
                enextself(*searchtet); // oppo is duplicated with p.
                loc = ONVERTEX; // return ONVERTEX;
                break;
              }
              loc = ONEDGE; // return ONEDGE;
              break;
            }
            if (oriapex == 0) {
              enext2self(*searchtet);
              loc = ONEDGE; // return ONEDGE;
              break;
            }
            loc = ONFACE; // return ONFACE;
            break;
          }
          if (oridest == 0) {
            // Go to the face opposite to destination.
            enext2fnextself(*searchtet);
            if (oriapex == 0) {
              enextself(*searchtet);
              loc = ONEDGE; // return ONEDGE;
              break;
            }
            loc = ONFACE; // return ONFACE;
            break;
          }
          if (oriapex == 0) {
            // Go to the face opposite to apex
            fnextself(*searchtet);
            loc = ONFACE; // return ONFACE;
            break;
          }
          loc = INTETRAHEDRON; // return INTETRAHEDRON;
          break;
        }
      }
    }
    
    // Move to the selected face.
    if (nextmove == ORGMOVE) {
      enextfnextself(*searchtet);
    } else if (nextmove == DESTMOVE) {
      enext2fnextself(*searchtet);
    } else {
      fnextself(*searchtet);
    }
    // Move to the adjacent tetrahedron (maybe a hull tetrahedron).
    backtracetet = *searchtet;
    symself(*searchtet);
    if (searchtet->tet == dummytet) {
      *searchtet = backtracetet;
      loc = OUTSIDE; // return OUTSIDE;
      break;
    }

    if (histtetarray != NULL) {
      // Check if we have run into a loop.
      if (infected(*searchtet)) {
        // We have visited this tet. A potential loop is found.
        loc = OUTSIDE;
        break;
      } else {
        // Remember this tet.
        infect(*searchtet);
        histtetarray->newindex((void **) &parytet);
        *parytet = *searchtet;
      }
    }

    // Retreat the three vertices of the base face.
    searchtet->ver = 0;
    torg = org(*searchtet);
    tdest = dest(*searchtet);
    tapex = apex(*searchtet);

  } // while (true)

  if (histtetarray != NULL) {
    // Unmark the visited tets.
    for (i = 0; i < (int) histtetarray->objects; i++) {
      parytet = (triface *) fastlookup(histtetarray, i);
      uninfect(*parytet);
    }
    histtetarray->restart();
  }

  return loc;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// adjustlocate()    Adjust the precise location of a vertex.                //
//                                                                           //
// 'precise' is the value returned from preciselocate().  It indicates the   //
// exact location of the point 'searchpt' with respect to the tetrahedron    //
// 'searchtet'.  'epspp' is a given relative tolerance.                      //
//                                                                           //
// This routine re-evaluates the orientations of searchpt with respect to    //
// the four sides of searchtet. Detects the coplanarities by additinal tests //
// which are based on the given tolerance. If 'precise' is ONFACE or ONEDGE, //
// we can save one or two orientation tests.                                 //
//                                                                           //
// The return value indicates the location of the 'searchpt' (INTETRAHEDRON, //
// or ONFACE, ...). 'searchtet' is adjusted to a tetrahedron corresponding   //
// to that value. See the introduction part of preciselocate().              //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

enum tetgenmesh::locateresult tetgenmesh::adjustlocate(point searchpt,
  triface* searchtet, enum locateresult precise, REAL epspp)
{
  point torg, tdest, tapex, toppo;
  REAL s1, s2, s3, s4;

  // For the given 'searchtet', the orientations tests are:
  //  s1: (tdest, torg, tapex, searchpt);
  //  s2: (torg, tdest, toppo, searchpt);
  //  s3: (tdest, tapex, toppo, searchpt);
  //  s4: (tapex, torg, toppo, searchpt);
  adjustedgering(*searchtet, CCW);
  torg = org(*searchtet);
  tdest = dest(*searchtet);
  tapex = apex(*searchtet);
  toppo = oppo(*searchtet);

  switch (precise) {
  case ONVERTEX:
    // This case we don't need do any further test.
    return ONVERTEX;
  case ONEDGE:
    // (torg, tdest);
    s1 = 0.0;
    s2 = 0.0;
    break;
  case ONFACE:
    // (tdest, torg, tapex);
    s1 = 0.0;
    s2 = orient3d(torg, tdest, toppo, searchpt);
    break;
  default: // INTETRAHEDRON or OUTSIDE
    s1 = orient3d(tdest, torg, tapex, searchpt);
    s2 = orient3d(torg, tdest, toppo, searchpt);
  }
  
  if (s1 != 0.0) {
    if (iscoplanar(tdest, torg, tapex, searchpt, s1, epspp)) {
      s1 = 0.0;
    }
  }
  if (s1 < 0.0) {
    return OUTSIDE;
  }

  if (s2 != 0.0) {
    if (iscoplanar(torg, tdest, toppo, searchpt, s2, epspp)) {
      s2 = 0.0;
    }
  }
  if (s2 < 0.0) {
    fnextself(*searchtet);
    return OUTSIDE;
  }

  s3 = orient3d(tdest, tapex, toppo, searchpt);
  if (s3 != 0.0) {
    if (iscoplanar(tdest, tapex, toppo, searchpt, s3, epspp)) {
      s3 = 0.0;
    }
  }
  if (s3 < 0.0) {
    enextfnextself(*searchtet);
    return OUTSIDE;
  }

  s4 = orient3d(tapex, torg, toppo, searchpt);
  if (s4 != 0.0) {
    if (iscoplanar(tapex, torg, toppo, searchpt, s4, epspp)) {
      s4 = 0.0;
    }
  }
  if (s4 < 0.0) {
    enext2fnextself(*searchtet);
    return OUTSIDE;
  }

  // Determine degenerate cases.
  if (s1 == 0.0) {
    if (s2 == 0.0) {
      if (s3 == 0.0) {
        // On tdest.
        enextself(*searchtet);
        return ONVERTEX;
      }
      if (s4 == 0.0) {
        // On torg.
        return ONVERTEX;
      }
      // On edge (torg, tdest).
      return ONEDGE;
    }
    if (s3 == 0.0) {
      if (s4 == 0.0) {
        // On tapex.
        enext2self(*searchtet);
        return ONVERTEX;
      }
      // On edge (tdest, tapex).
      enextself(*searchtet);
      return ONEDGE;
    }
    if (s4 == 0.0) {
      // On edge (tapex, torg).
      enext2self(*searchtet);
      return ONEDGE;
    }
    // On face (torg, tdest, tapex).
    return ONFACE;
  }
  if (s2 == 0.0) {
    fnextself(*searchtet);
    if (s3 == 0.0) {
      if (s4 == 0.0) {
        // On toppo.
        enext2self(*searchtet);
        return ONVERTEX;
      }
      // On edge (tdest, toppo).
      enextself(*searchtet);
      return ONEDGE;
    }
    if (s4 == 0.0) {
      // On edge (toppo, torg).
      enext2self(*searchtet);
      return ONEDGE;
    }
    // On face (torg, tdest, toppo).
    return ONFACE;
  }
  if (s3 == 0.0) {
    enextfnextself(*searchtet);
    if (s4 == 0.0) {
      // On edge (tapex, toppo).
      enextself(*searchtet);
      return ONEDGE;
    }
    // On face (tdest, tapex, toppo).
    return ONFACE;
  }
  if (s4 == 0.0) {
    enext2fnextself(*searchtet);
    // On face (tapex, torg, toppo).
    return ONFACE;
  }

  // Inside tetrahedron.
  return INTETRAHEDRON;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// hullwalk()    Find a tetrahedron on the hull to continue search.          //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

enum tetgenmesh::locateresult tetgenmesh::hullwalk(point searchpt,
  triface *hulltet)
{
  list* travtetlist;
  triface travtet, neightet;
  point pa, pb, pc, pp[3];
  enum locateresult loc;
  REAL prjpt[3];
  REAL ori;
  int i, j;

  travtetlist = new list(sizeof(triface), NULL, 256);
  travtet = *hulltet;
  infect(travtet);
  travtetlist->append(&travtet);

  loc = OUTSIDE;
  for (i = 0; i < travtetlist->len(); i++) {
    travtet = * (triface *)(* travtetlist)[i];
    // Choose the CCW-edgering in face.
    travtet.ver = 0;
    // Look for a side where pt lies below it.
    for (travtet.loc = 0; travtet.loc < 4; travtet.loc++) {
      pa = org(travtet);
      pb = dest(travtet);
      pc = apex(travtet);
      ori = orient3d(pa, pb, pc, searchpt);
      if (ori > 0.0) break;
    }
    // Is pt above all (or coplanar with some of) the four sides?
    if (travtet.loc == 4) {
      hulltet->tet = travtet.tet;
      loc = adjustlocate(searchpt, hulltet, INTETRAHEDRON, b->epsilon);
      //assert(loc != OUTSIDE);
    } else { // ori > 0.0
      // pt is below (behind) this side. We want to walk through it.
      sym(travtet, neightet);
      if (neightet.tet == dummytet) {
        // This is a hull side. Is p approximately on this side.
        loc = adjustlocate(searchpt, &travtet, OUTSIDE, b->epsilon);
      }
      if (loc == OUTSIDE) {
        // searchpt is outside the hull face. Project it on the face.
        travtet.ver = 1;
        pp[0] = org(travtet);
        pp[1] = dest(travtet);
        pp[2] = apex(travtet);
        projpt2face(searchpt, pp[0], pp[1], pp[2], prjpt);
        // check if project point inside the hull face.
        for (j = 0; j < 3; j++) {
          ori = orient3d(pp[j], pp[(j+1)%3], searchpt, prjpt);
          if (ori < 0.0) break; // Stop if it lies ouside.
        }
        if (ori >= 0.0) {
          // Yes, return this tet.
          *hulltet = travtet;
        }
        // Let's collect all the neighbors for next searching.
        for (travtet.loc = 0; travtet.loc < 4; travtet.loc++) {
          sym(travtet, neightet);
          if ((neightet.tet != dummytet) && !infected(neightet)) {
            // Neighbor exists and not visited.
            infect(neightet);
            travtetlist->append(&neightet);
          }
        } // for (travtet.loc = 0; 
      } // if (loc == OUTSIDE)
    } // if (travtet.loc == 4)
    if (loc != OUTSIDE) break;
  } // for (i = 0; i < travtetlist->len(); i++)
  
  // Uninfect traversed tets.
  for (i = 0; i < travtetlist->len(); i++) {
    travtet = * (triface *)(* travtetlist)[i];
    uninfect(travtet);
  }

  delete travtetlist;
  return loc;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// locatesub()    Find a point in the surface mesh of a facet.               //
//                                                                           //
// Searching begins from the input 'searchsh', it should be a handle on the  //
// convex hull of the facet triangulation.                                   //
//                                                                           //
// If 'stopatseg' is nonzero, the search will stop if it tries to walk       //
// through a subsegment, and will return OUTSIDE.                            //
//                                                                           //
// On completion, 'searchsh' is a subface that contains 'searchpt'.          //
//   - Returns ONVERTEX if the point lies on an existing vertex. 'searchsh'  //
//     is a handle whose origin is the existing vertex.                      //
//   - Returns ONEDGE if the point lies on a mesh edge.  'searchsh' is a     //
//     handle whose primary edge is the edge on which the point lies.        //
//   - Returns ONFACE if the point lies strictly within a subface.           //
//     'searchsh' is a handle on which the point lies.                       //
//   - Returns OUTSIDE if the point lies outside the triangulation.          //
//                                                                           //
// WARNING: This routine is designed for convex triangulations, and will not //
// not generally work after the holes and concavities have been carved.      //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

enum tetgenmesh::locateresult tetgenmesh::locatesub(point searchpt,
  face* searchsh, int stopatseg, REAL epspp)
{
  face backtracksh, spinsh, checkedge;
  point forg, fdest, fapex;
  REAL orgori, destori;
  REAL ori, sign;
  int moveleft, i;

  if (searchsh->sh == dummysh) {
    searchsh->shver = 0;
    spivotself(*searchsh);
#ifdef SELF_CHECK
    //assert(searchsh->sh != dummysh);
#endif
  }
  // Find the sign to simulate that abovepoint is 'above' the facet.
  adjustedgering(*searchsh, CCW);
  forg = sorg(*searchsh);
  fdest = sdest(*searchsh);
  fapex = sapex(*searchsh);
  ori = orient3d(forg, fdest, fapex, abovepoint);
  sign = ori > 0.0 ? -1 : 1;

  // Orient 'searchsh' so that 'searchpt' is below it (i.e., searchpt has
  //   CCW orientation with respect to searchsh in plane).  Such edge
  //   should always exist. Save it as (forg, fdest).
  for (i = 0; i < 3; i++) {
    forg = sorg(*searchsh);
    fdest = sdest(*searchsh);
    ori = orient3d(forg, fdest, abovepoint, searchpt) * sign;
    if (ori > 0.0) break;
    senextself(*searchsh);
  }
#ifdef SELF_CHECK
  //assert(i < 3);
#endif
  
  while (1) {
    fapex = sapex(*searchsh);
    // Check whether the apex is the point we seek.
    if (fapex[0] == searchpt[0] && fapex[1] == searchpt[1] &&
        fapex[2] == searchpt[2]) {
      senext2self(*searchsh);
      return ONVERTEX;
    }
    // Does the point lie on the other side of the line defined by the
    //   triangle edge opposite the triangle's destination?
    destori = orient3d(forg, fapex, abovepoint, searchpt) * sign;
    if (epspp > 0.0) {
      if (iscoplanar(forg, fapex, abovepoint, searchpt, destori, epspp)) {
        destori = 0.0;
      }
    }
    // Does the point lie on the other side of the line defined by the
    //   triangle edge opposite the triangle's origin? 
    orgori = orient3d(fapex, fdest, abovepoint, searchpt) * sign;
    if (epspp > 0.0) {
      if (iscoplanar(fapex, fdest, abovepoint, searchpt, orgori, epspp)) {
        orgori = 0.0;
      }
    }
    if (destori > 0.0) {
      moveleft = 1;
    } else {
      if (orgori > 0.0) {
        moveleft = 0;
      } else {
        // The point must be on the boundary of or inside this triangle.
        if (destori == 0.0) {
          senext2self(*searchsh);
          return ONEDGE;
        } 
        if (orgori == 0.0) {
          senextself(*searchsh);
          return ONEDGE;
        }
        return ONFACE;
      }
    }
    // Move to another triangle.  Leave a trace `backtracksh' in case
    //   walking off a boundary of the triangulation.
    if (moveleft) {
      senext2(*searchsh, backtracksh);
      fdest = fapex;
    } else {
      senext(*searchsh, backtracksh);
      forg = fapex;
    }
    // Check if we meet a segment.
    sspivot(backtracksh, checkedge);
    if (checkedge.sh != dummysh) {
      if (stopatseg) {
        // The flag indicates we should not cross a segment. Stop.
        *searchsh = backtracksh;
        return OUTSIDE;
      }
      // Try to walk through a segment. We need to find a coplanar subface
      //   sharing this segment to get into.
      spinsh = backtracksh;
      do {
        spivotself(spinsh);
        if (spinsh.sh == backtracksh.sh) {
          // Turn back, no coplanar subface is found.
          break;
        }
        // Are they belong to the same facet.
        if (shellmark(spinsh) == shellmark(backtracksh)) {
          // Find a coplanar subface. Walk into it.
          *searchsh = spinsh;
          break;
        }
        // Are they (nearly) coplanar?
        ori = orient3d(forg, fdest, sapex(backtracksh), sapex(spinsh));
        if (iscoplanar(forg, fdest, sapex(backtracksh), sapex(spinsh), ori,
                       b->epsilon)) {
          // Find a coplanar subface. Walk into it.
          *searchsh = spinsh;
          break;
        }
      } while (spinsh.sh != backtracksh.sh);
    } else {
      spivot(backtracksh, *searchsh);
    }
    // Check for walking right out of the triangulation.
    if ((searchsh->sh == dummysh) || (searchsh->sh == backtracksh.sh)) {
      // Go back to the last triangle.
      *searchsh = backtracksh;
      return OUTSIDE;
    }
    // To keep the same orientation wrt abovepoint.
    if (sorg(*searchsh) != forg) sesymself(*searchsh);
#ifdef SELF_CHECK
    //assert((sorg(*searchsh) == forg) && (sdest(*searchsh) == fdest));
#endif
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// adjustlocatesub()    Adjust the precise location of a vertex.             //
//                                                                           //
// 'precise' is the precise location (returned from locatesub()) of 'searcht'//
// with respect to 'searchsh'. 'epspp' is the given relative tolerance.      //
//                                                                           //
// This routine re-evaluates the orientations of 'searchpt' with respect to  //
// the three edges of 'searchsh'. Detects the collinearities by additinal    //
// tests based on the given tolerance. If 'precise' is ONEDGE, one can save  //
// one orientation test for the current edge of 'searchsh'.                  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

enum tetgenmesh::locateresult tetgenmesh::
adjustlocatesub(point searchpt, face* searchsh, enum locateresult precise,
                REAL epspp)
{
  point pa, pb, pc;
  bool s1, s2, s3;

  pa = sorg(*searchsh);
  pb = sdest(*searchsh);
  pc = sapex(*searchsh);

  if (precise == ONEDGE) {
    s1 = true;
  } else {
    s1 = iscollinear(pa, pb, searchpt, epspp);
  }
  s2 = iscollinear(pb, pc, searchpt, epspp);
  s3 = iscollinear(pc, pa, searchpt, epspp);
  if (s1) {
    if (s2) {
      // on vertex pb.
#ifdef SELF_CHECK
      //assert(!s3);
#endif
      senextself(*searchsh);
      return ONVERTEX;
    } else if (s3) {
      // on vertex pa.
      return ONVERTEX;
    } else {
      // on edge pa->pb.
      return ONEDGE;
    }
  } else if (s2) {
    if (s3) {
      // on vertex pc.
      senext2self(*searchsh);
      return ONVERTEX;
    } else {
      // on edge pb->pc.
      senextself(*searchsh);
      return ONEDGE;
    }
  } else if (s3) {
    // on edge pc->pa.
    senext2self(*searchsh);
    return ONEDGE;
  } else {
    return precise;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// locateseg()    Find a point in subsegments.                               //
//                                                                           //
// Searching begins from the input 'searchseg', it should be a subsegment of //
// the whole segment.                                                        //
//                                                                           //
// On completion, 'searchseg' is a subsegment that contains 'searchpt'.      //
//   - Returns ONVERTEX if the point lies on an existing vertex. 'searchseg' //
//     is a handle whose origin is the existing vertex.                      //
//   - Returns ONEDGE if the point lies inside 'searchseg'.                  //
//   - Returns OUTSIDE if the point lies outside the segment.                //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

enum tetgenmesh::locateresult tetgenmesh::
locateseg(point searchpt, face* searchseg)
{
  face backtraceseg;
  point pa, pb;
  REAL dx, dy, dz;
  int moveleft;
  int i;

  moveleft = 0;
  while (1) {
    searchseg->shver = 0;
    pa = sorg(*searchseg);
    pb = sdest(*searchseg);
    // Find the biggest difference in x, y, and z coordinates of a and b.
    dx = fabs(pb[0] - pa[0]);
    dy = fabs(pb[1] - pa[1]);
    dz = fabs(pb[2] - pa[2]);
    if (dx > dy) {
      if (dx > dz) {
        i = 0;
      } else {
        i = 2;
      }
    } else {
      if (dy > dz) {
        i = 1;
      } else {
        i = 2;
      }
    }
    if (pa[i] < pb[i]) {
      if (searchpt[i] < pa[i]) {
        moveleft = 1;
      } else if (searchpt[i] > pa[i]) {
        if (searchpt[i] < pb[i]) {
          return ONEDGE;
        } else if (searchpt[i] > pb[i]) {
          moveleft = 0;
        } else {
#ifdef SELF_CHECK
          //assert(searchpt[i] == pb[i]);
#endif
          sesymself(*searchseg);
          return ONVERTEX;
        }
      } else {
#ifdef SELF_CHECK
        //assert(searchpt[i] == pa[i]);
#endif
        return ONVERTEX;
      }
    } else if (pa[i] > pb[i]) {
      if (searchpt[i] < pb[i]) {
        moveleft = 0;
      } else if (searchpt[i] > pb[i]) {
        if (searchpt[i] < pa[i]) {
          return ONEDGE;
        } else if (searchpt[i] > pa[i]) {
          moveleft = 1;
        } else {
#ifdef SELF_CHECK
          //assert(searchpt[i] == pa[i]);
#endif
          return ONVERTEX;
        }
      } else {
#ifdef SELF_CHECK
        //assert(searchpt[i] == pb[i]);
#endif
        sesymself(*searchseg);
        return ONVERTEX;
      }
    }
    backtraceseg = *searchseg;
    if (moveleft) {
      senext2self(*searchseg);
    } else {
      senextself(*searchseg);
    }
    spivotself(*searchseg);
    if (searchseg->sh == dummysh) {
      *searchseg = backtraceseg;
      break;
    }
  }

  return OUTSIDE;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// adjustlocateseg()    Adjust the precise location of a vertex on segment.  //
//                                                                           //
// 'searchpt' is either inside or ouside the segment 'searchseg'. It will be //
// adjusted to on vertex if it is very close to an endpoint of 'searchseg'.  //
// 'epspp' is the given relative tolerance.                                  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

enum tetgenmesh::locateresult tetgenmesh::
adjustlocateseg(point searchpt, face* searchseg, enum locateresult precise,
                REAL epspp)
{
  point pa, pb;
  REAL L, d, r;

  pa = sorg(*searchseg);
  pb = sdest(*searchseg);
  L = distance(pa, pb);

  // Is searchpt approximate to pa?
  d = distance(pa, searchpt);
  r = d / L;
  if (r <= epspp) {
    return ONVERTEX;
  }
  // Is searchpt approximate to pb?
  d = distance(pb, searchpt);
  r = d / L;
  if (r <= epspp) {
    sesymself(*searchseg);
    return ONVERTEX;
  }

  return precise;
}

////                                                                       ////
////                                                                       ////
//// geom_cxx /////////////////////////////////////////////////////////////////

//// flip_cxx /////////////////////////////////////////////////////////////////
////                                                                       ////
////                                                                       ////

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// enqueueflipface(), enqueueflipedge()    Queue a face (or an edge).        //
//                                                                           //
// The face (or edge) may be non-locally Delaunay. It is queued for process- //
// ing in flip() (or flipsub()). The vertices of the face (edge) are stored  //
// seperatly to ensure the face (or edge) is still the same one when we save //
// it since other flips will cause this face (or edge) be changed or dead.   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::enqueueflipface(triface& checkface, queue* flipqueue)
{
  badface *queface;
  triface symface;

  sym(checkface, symface);
  if (symface.tet != dummytet) {
    queface = (badface *) flipqueue->push((void *) NULL);
    queface->tt = checkface;
    queface->foppo = oppo(symface);
  }
}

void tetgenmesh::enqueueflipedge(face& checkedge, queue* flipqueue)
{
  badface *queface;

  queface = (badface *) flipqueue->push((void *) NULL);
  queface->ss = checkedge;
  queface->forg = sorg(checkedge);
  queface->fdest = sdest(checkedge);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// flip23()    Perform a 2-to-3 flip.                                        //
//                                                                           //
// On input, 'flipface' represents the face will be flipped.  Let it is abc, //
// the two tetrahedra sharing abc are abcd, bace. abc is not a subface.      //
//                                                                           //
// A 2-to-3 flip is to change two tetrahedra abcd, bace to three tetrahedra  //
// edab, edbc, and edca.  As a result, face abc has been removed and three   //
// new faces eda, edb and edc have been created.                             //
//                                                                           //
// On completion, 'flipface' returns edab.  If 'flipqueue' is not NULL, all  //
// possibly non-Delaunay faces are added into it.                            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::flip23(triface* flipface, queue* flipqueue)
{
  triface abcd, bace;                                  // Old configuration.
  triface oldabd, oldbcd, oldcad;
  triface abdcasing, bcdcasing, cadcasing;
  triface oldbae, oldcbe, oldace;
  triface baecasing, cbecasing, acecasing;
  triface worktet;
  face abdsh, bcdsh, cadsh;                   // The six subfaces on the CH.
  face baesh, cbesh, acesh;
  face abseg, bcseg, caseg;                      // The nine segs on the CH.
  face adseg, bdseg, cdseg;
  face aeseg, beseg, ceseg;
  triface edab, edbc, edca;                            // New configuration.
  point pa, pb, pc, pd, pe;
  REAL attrib, volume;
  int i;

  abcd = *flipface;
  adjustedgering(abcd, CCW); // abcd represents edge ab.
  pa = org(abcd);
  pb = dest(abcd);
  pc = apex(abcd);
  pd = oppo(abcd);
  // sym(abcd, bace);
  // findedge(&bace, dest(abcd), org(abcd)); // bace represents edge ba.
  sym(abcd, bace);
  bace.ver = 0; // CCW.
  for (i = 0; (i < 3) && (org(bace) != pb); i++) {
    enextself(bace);
  }
  pe = oppo(bace);

  if (b->verbose > 1) {
    //printf("    Do T23 on face (%d, %d, %d) %d, %d.\n", pointmark(pa),
           //pointmark(pb), pointmark(pc), pointmark(pd), pointmark(pe));
  }
  flip23s++;

  // Storing the old configuration outside the convex hull.
  fnext(abcd, oldabd);
  enextfnext(abcd, oldbcd);
  enext2fnext(abcd, oldcad);
  fnext(bace, oldbae);
  enext2fnext(bace, oldcbe);
  enextfnext(bace, oldace);
  sym(oldabd, abdcasing);
  sym(oldbcd, bcdcasing);
  sym(oldcad, cadcasing);
  sym(oldbae, baecasing);
  sym(oldcbe, cbecasing);
  sym(oldace, acecasing);
  if (checksubfaces) {
    tspivot(oldabd, abdsh);
    tspivot(oldbcd, bcdsh);
    tspivot(oldcad, cadsh);
    tspivot(oldbae, baesh);
    tspivot(oldcbe, cbesh);
    tspivot(oldace, acesh);
  } 
  if (checksubsegs) {
    tsspivot1(abcd, abseg);
    enext(abcd, worktet);
    tsspivot1(worktet, bcseg);
    enext2(abcd, worktet);
    tsspivot1(worktet, caseg);
    enext2(oldabd, worktet);
    tsspivot1(worktet, adseg);
    enext2(oldbcd, worktet);
    tsspivot1(worktet, bdseg);
    enext2(oldcad, worktet);
    tsspivot1(worktet, cdseg);
    enext(oldbae, worktet);
    tsspivot1(worktet, aeseg);
    enext(oldcbe, worktet);
    tsspivot1(worktet, beseg);
    enext(oldace, worktet);
    tsspivot1(worktet, ceseg);
  }

  // Creating the new configuration inside the convex hull.
  edab.tet = abcd.tet; // Update abcd to be edab.
  setorg (edab, pe);
  setdest(edab, pd);
  setapex(edab, pa);
  setoppo(edab, pb);
  edbc.tet = bace.tet; // Update bace to be edbc.
  setorg (edbc, pe);
  setdest(edbc, pd);
  setapex(edbc, pb);
  setoppo(edbc, pc);
  maketetrahedron(&edca); // Create edca.
  setorg (edca, pe);
  setdest(edca, pd);
  setapex(edca, pc);
  setoppo(edca, pa);
  // Set the element attributes of the new tetrahedron 'edca'.
  for (i = 0; i < in->numberoftetrahedronattributes; i++) {
    attrib = elemattribute(abcd.tet, i);
    setelemattribute(edca.tet, i, attrib);
  }
  // Set the volume constraint of the new tetrahedron 'edca' if the -ra
  //   switches are not used together. In -ra case, the various volume
  //   constraints can be spreaded very far.
  if (b->varvolume && !b->refine) {
    volume = volumebound(abcd.tet);
    setvolumebound(edca.tet, volume);
  }

  // Clear old bonds in edab(was abcd) and edbc(was bace).
  for (i = 0; i < 4; i ++) {
    edab.tet[i] = (tetrahedron) dummytet;
  }
  for (i = 0; i < 4; i ++) {
    edbc.tet[i] = (tetrahedron) dummytet;
  }
  // Bond the faces inside the convex hull.
  edab.loc = 0;
  edca.loc = 1;
  bond(edab, edca);
  edab.loc = 1;
  edbc.loc = 0;
  bond(edab, edbc);
  edbc.loc = 1;
  edca.loc = 0;
  bond(edbc, edca);
  // Bond the faces on the convex hull.
  edab.loc = 2;
  bond(edab, abdcasing);
  edab.loc = 3;
  bond(edab, baecasing);
  edbc.loc = 2;
  bond(edbc, bcdcasing);
  edbc.loc = 3;
  bond(edbc, cbecasing);
  edca.loc = 2;
  bond(edca, cadcasing);
  edca.loc = 3;
  bond(edca, acecasing);
  // There may exist subfaces that need to be bonded to new configuarton.
  if (checksubfaces) {
    // Clear old flags in edab(was abcd) and edbc(was bace).
    for (i = 0; i < 4; i ++) {
      edab.loc = i;
      tsdissolve(edab);
      edbc.loc = i;
      tsdissolve(edbc);
    }
    if (abdsh.sh != dummysh) {
      edab.loc = 2; 
      tsbond(edab, abdsh);
    }
    if (baesh.sh != dummysh) {
      edab.loc = 3; 
      tsbond(edab, baesh);
    }
    if (bcdsh.sh != dummysh) {
      edbc.loc = 2; 
      tsbond(edbc, bcdsh);
    }
    if (cbesh.sh != dummysh) {
      edbc.loc = 3; 
      tsbond(edbc, cbesh);
    }
    if (cadsh.sh != dummysh) {
      edca.loc = 2; 
      tsbond(edca, cadsh);
    }
    if (acesh.sh != dummysh) {
      edca.loc = 3; 
      tsbond(edca, acesh);
    }
  } 
  if (checksubsegs) {
    for (i = 0; i < 6; i++) {
      edab.loc = edge2locver[i][0];
      edab.ver = edge2locver[i][1];
      tssdissolve1(edab);
    }
    for (i = 0; i < 6; i++) {
      edbc.loc = edge2locver[i][0];
      edbc.ver = edge2locver[i][1];
      tssdissolve1(edbc);
    }
    edab.loc = edab.ver = 0;
    edbc.loc = edab.ver = 0;
    edca.loc = edab.ver = 0;
    // Operate in tet edab (5 edges).
    enext(edab, worktet);
    tssbond1(worktet, adseg);
    enext2(edab, worktet);
    tssbond1(worktet, aeseg);
    fnext(edab, worktet);
    enextself(worktet);
    tssbond1(worktet, bdseg);
    enextself(worktet);
    tssbond1(worktet, beseg);
    enextfnext(edab, worktet);
    enextself(worktet);
    tssbond1(worktet, abseg);
    // Operate in tet edbc (5 edges)
    enext(edbc, worktet);
    tssbond1(worktet, bdseg);
    enext2(edbc, worktet);
    tssbond1(worktet, beseg);
    fnext(edbc, worktet);
    enextself(worktet);
    tssbond1(worktet, cdseg);
    enextself(worktet);
    tssbond1(worktet, ceseg);
    enextfnext(edbc, worktet);
    enextself(worktet);
    tssbond1(worktet, bcseg);
    // Operate in tet edca (5 edges)
    enext(edca, worktet);
    tssbond1(worktet, cdseg);
    enext2(edca, worktet);
    tssbond1(worktet, ceseg);
    fnext(edca, worktet);
    enextself(worktet);
    tssbond1(worktet, adseg);
    enextself(worktet);
    tssbond1(worktet, aeseg);
    enextfnext(edca, worktet);
    enextself(worktet);
    tssbond1(worktet, caseg);
  }

  edab.loc = 0;
  edbc.loc = 0;
  edca.loc = 0;
  if (b->verbose > 3) {
    //printf("    Updating edab ");
    printtet(&edab);
    //printf("    Updating edbc ");
    printtet(&edbc);
    //printf("    Creating edca ");
    printtet(&edca);
  }

  // Update point-to-tet map.
  setpoint2tet(pa, encode(edab));
  setpoint2tet(pb, encode(edab));
  setpoint2tet(pc, encode(edbc));
  setpoint2tet(pd, encode(edab));
  setpoint2tet(pe, encode(edab));

  if (flipqueue != (queue *) NULL) {
    enextfnext(edab, abdcasing);
    enqueueflipface(abdcasing, flipqueue);
    enext2fnext(edab, baecasing);
    enqueueflipface(baecasing, flipqueue);
    enextfnext(edbc, bcdcasing);
    enqueueflipface(bcdcasing, flipqueue);
    enext2fnext(edbc, cbecasing);
    enqueueflipface(cbecasing, flipqueue);
    enextfnext(edca, cadcasing);
    enqueueflipface(cadcasing, flipqueue);
    enext2fnext(edca, acecasing);
    enqueueflipface(acecasing, flipqueue);  
  }

  // Save a live handle in 'recenttet'.
  recenttet = edbc;
  // Set the return handle be edab.
  *flipface = edab;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// flip32()    Perform a 3-to-2 flip.                                        //
//                                                                           //
// On input, 'flipface' represents the face will be flipped.  Let it is eda, //
// where edge ed is locally non-convex. Three tetrahedra sharing ed are edab,//
// edbc, and edca.  ed is not a subsegment.                                  //
//                                                                           //
// A 3-to-2 flip is to change the three tetrahedra edab, edbc, and edca into //
// another two tetrahedra abcd and bace.  As a result, the edge ed has been  //
// removed and the face abc has been created.                                //
//                                                                           //
// On completion, 'flipface' returns abcd.  If 'flipqueue' is not NULL, all  //
// possibly non-Delaunay faces are added into it.                            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::flip32(triface* flipface, queue* flipqueue)
{
  triface edab, edbc, edca;                            // Old configuration.
  triface oldabd, oldbcd, oldcad;
  triface abdcasing, bcdcasing, cadcasing;
  triface oldbae, oldcbe, oldace;
  triface baecasing, cbecasing, acecasing;
  triface worktet;
  face abdsh, bcdsh, cadsh;
  face baesh, cbesh, acesh;
  face abseg, bcseg, caseg;                      // The nine segs on the CH.
  face adseg, bdseg, cdseg;
  face aeseg, beseg, ceseg;
  triface abcd, bace;                                  // New configuration.
  point pa, pb, pc, pd, pe;
  int i;

  edab = *flipface;
  adjustedgering(edab, CCW);
  pa = apex(edab);
  pb = oppo(edab);
  pd = dest(edab);
  pe = org(edab);
  fnext(edab, edbc);
  symself(edbc);
  edbc.ver = 0;
  for (i = 0; (i < 3) && (org(edbc) != pe); i++) {
    enextself(edbc);
  }
  pc = oppo(edbc);
  fnext(edbc, edca);
  symself(edca);
  edca.ver = 0;
  for (i = 0; (i < 3) && (org(edca) != pe); i++) {
    enextself(edca);
  }

  if (b->verbose > 1) {
    //printf("    Do T32 on edge (%d, %d) %d, %d, %d.\n", pointmark(pe),
           //pointmark(pd), pointmark(pa), pointmark(pb), pointmark(pc));
  }
  flip32s++;

  // Storing the old configuration outside the convex hull.
  enextfnext(edab, oldabd);
  enext2fnext(edab, oldbae);
  enextfnext(edbc, oldbcd);
  enext2fnext(edbc, oldcbe);
  enextfnext(edca, oldcad);
  enext2fnext(edca, oldace);
  sym(oldabd, abdcasing);
  sym(oldbcd, bcdcasing);
  sym(oldcad, cadcasing);
  sym(oldbae, baecasing);
  sym(oldcbe, cbecasing);
  sym(oldace, acecasing);
  if (checksubfaces) {
    tspivot(oldabd, abdsh);
    tspivot(oldbcd, bcdsh);
    tspivot(oldcad, cadsh);
    tspivot(oldbae, baesh);
    tspivot(oldcbe, cbesh);
    tspivot(oldace, acesh);
  } 
  if (checksubsegs) {
    enext(edab, worktet);
    tsspivot1(worktet, adseg);
    enext2(edab, worktet);
    tsspivot1(worktet, aeseg);
    enext(edbc, worktet);
    tsspivot1(worktet, bdseg);
    enext2(edbc, worktet);
    tsspivot1(worktet, beseg);
    enext(edca, worktet);
    tsspivot1(worktet, cdseg);
    enext2(edca, worktet);
    tsspivot1(worktet, ceseg);
    enextfnext(edab, worktet);
    enextself(worktet);
    tsspivot1(worktet, abseg);
    enextfnext(edbc, worktet);
    enextself(worktet);
    tsspivot1(worktet, bcseg);
    enextfnext(edca, worktet);
    enextself(worktet);
    tsspivot1(worktet, caseg);
  }

  // Creating the new configuration inside the convex hull.
  abcd.tet = edab.tet; // Update edab to be abcd.
  setorg (abcd, pa);
  setdest(abcd, pb);
  setapex(abcd, pc);
  setoppo(abcd, pd);
  bace.tet = edbc.tet; // Update edbc to be bace.
  setorg (bace, pb);
  setdest(bace, pa);
  setapex(bace, pc);
  setoppo(bace, pe);
  // Dealloc a redundant tetrahedron (edca).
  tetrahedrondealloc(edca.tet); 

  // Clear the old bonds in abcd (was edab) and bace (was edbc).
  for (i = 0; i < 4; i ++) {
    abcd.tet[i] = (tetrahedron) dummytet;
  }
  for (i = 0; i < 4; i ++) {
    bace.tet[i] = (tetrahedron) dummytet;
  }
  // Bond the inside face of the convex hull.
  abcd.loc = 0;
  bace.loc = 0;
  bond(abcd, bace);
  // Bond the outside faces of the convex hull.
  abcd.loc = 1;
  bond(abcd, abdcasing);
  abcd.loc = 2;
  bond(abcd, bcdcasing);
  abcd.loc = 3;
  bond(abcd, cadcasing);
  bace.loc = 1;
  bond(bace, baecasing);
  bace.loc = 3;
  bond(bace, cbecasing);
  bace.loc = 2;
  bond(bace, acecasing);
  if (checksubfaces) {
    // Clear old bonds in abcd(was edab) and bace(was edbc).
    for (i = 0; i < 4; i ++) {
      abcd.loc = i;
      tsdissolve(abcd);
    }
    for (i = 0; i < 4; i ++) {
      bace.loc = i;
      tsdissolve(bace);
    }
    if (abdsh.sh != dummysh) {
      abcd.loc = 1;
      tsbond(abcd, abdsh);
    }
    if (bcdsh.sh != dummysh) {
      abcd.loc = 2;
      tsbond(abcd, bcdsh);
    }
    if (cadsh.sh != dummysh) {
      abcd.loc = 3;
      tsbond(abcd, cadsh);
    }
    if (baesh.sh != dummysh) {
      bace.loc = 1;
      tsbond(bace, baesh);
    }
    if (cbesh.sh != dummysh) {
      bace.loc = 3;
      tsbond(bace, cbesh);
    }
    if (acesh.sh != dummysh) {
      bace.loc = 2;
      tsbond(bace, acesh);
    }
  } 
  if (checksubsegs) {
    for (i = 0; i < 6; i++) {
      abcd.loc = edge2locver[i][0];
      abcd.ver = edge2locver[i][1];
      tssdissolve1(abcd);
    }
    for (i = 0; i < 6; i++) {
      bace.loc = edge2locver[i][0];
      bace.ver = edge2locver[i][1];
      tssdissolve1(bace);
    }
    abcd.loc = abcd.ver = 0;
    bace.loc = bace.ver = 0;
    tssbond1(abcd, abseg);     // 1
    enext(abcd, worktet);
    tssbond1(worktet, bcseg);  // 2
    enext2(abcd, worktet);
    tssbond1(worktet, caseg);  // 3
    fnext(abcd, worktet);
    enext2self(worktet);
    tssbond1(worktet, adseg);  // 4
    enextfnext(abcd, worktet);
    enext2self(worktet);
    tssbond1(worktet, bdseg);  // 5
    enext2fnext(abcd, worktet);
    enext2self(worktet);
    tssbond1(worktet, cdseg);  // 6
    tssbond1(bace, abseg);
    enext2(bace, worktet);
    tssbond1(worktet, bcseg);
    enext(bace, worktet);
    tssbond1(worktet, caseg);
    fnext(bace, worktet);
    enextself(worktet);
    tssbond1(worktet, aeseg);  // 7
    enext2fnext(bace, worktet);
    enextself(worktet);
    tssbond1(worktet, beseg);  // 8
    enextfnext(bace, worktet);
    enextself(worktet);
    tssbond1(worktet, ceseg);  // 9
  }

  abcd.loc = 0;
  bace.loc = 0;
  if (b->verbose > 3) {
    //printf("    Updating abcd ");
    printtet(&abcd);
    //printf("    Updating bace ");
    printtet(&bace);
    //printf("    Deleting edca ");
    // printtet(&edca);
  }

  // Update point-to-tet map.
  setpoint2tet(pa, encode(abcd));
  setpoint2tet(pb, encode(abcd));
  setpoint2tet(pc, encode(abcd));
  setpoint2tet(pd, encode(abcd));
  setpoint2tet(pe, encode(bace));

  if (flipqueue != (queue *) NULL) { 
    fnext(abcd, abdcasing);
    enqueueflipface(abdcasing, flipqueue);
    fnext(bace, baecasing);
    enqueueflipface(baecasing, flipqueue);
    enextfnext(abcd, bcdcasing);
    enqueueflipface(bcdcasing, flipqueue);
    enextfnext(bace, cbecasing);
    enqueueflipface(cbecasing, flipqueue);
    enext2fnext(abcd, cadcasing);
    enqueueflipface(cadcasing, flipqueue);
    enext2fnext(bace, acecasing);
    enqueueflipface(acecasing, flipqueue);  
  }

  // Save a live handle in 'recenttet'.
  recenttet = abcd;
  // Set the return handle be abcd.
  *flipface = abcd;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// flip22()    Perform a 2-to-2 (or 4-to-4) flip.                            //
//                                                                           //
// On input, 'flipface' represents the face will be flipped.  Let it is abe, //
// ab is the flipable edge, the two tetrahedra sharing abe are abce and bade,//
// hence a, b, c and d are coplanar. If abc, bad are interior faces, the two //
// tetrahedra opposite to e are bacf and abdf.  ab is not a subsegment.      //
//                                                                           //
// A 2-to-2 flip is to change two tetrahedra abce and bade into another two  //
// tetrahedra dcae and cdbe. If bacf and abdf exist, they're changed to cdaf //
// and dcbf, thus a 4-to-4 flip.  As a result, two or four tetrahedra have   //
// rotated counterclockwise (using right-hand rule with thumb points to e):  //
// abce->dcae, bade->cdbe, and bacf->cdaf, abdf->dcbf.                       //
//                                                                           //
// If abc and bad are subfaces, a 2-to-2 flip is performed simultaneously by //
// calling routine flip22sub(), hence abc->dca, bad->cdb.  The edge rings of //
// the flipped subfaces dca and cdb have the same orientation as abc and bad.//
// Hence, they have the same orientation as other subfaces of the facet with //
// respect to the lift point of this facet.                                  //
//                                                                           //
// On completion, 'flipface' holds edge dc of tetrahedron dcae. 'flipqueue'  //
// contains all possibly non-Delaunay faces if it is not NULL.               //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::flip22(triface* flipface, queue* flipqueue)
{
  triface abce, bade;
  triface oldbce, oldcae, oldade, olddbe;
  triface bcecasing, caecasing, adecasing, dbecasing;
  face bcesh, caesh, adesh, dbesh;
  triface bacf, abdf;
  triface oldacf, oldcbf, oldbdf, olddaf;
  triface acfcasing, cbfcasing, bdfcasing, dafcasing;
  triface worktet;
  face acfsh, cbfsh, bdfsh, dafsh;
  face abc, bad;
  face adseg, dbseg, bcseg, caseg;  // Coplanar segs.
  face aeseg, deseg, beseg, ceseg;  // Above segs.
  face afseg, dfseg, bfseg, cfseg;  // Below segs.
  point pa, pb, pc, pd, pe, pf;
  int mirrorflag, i;

  adjustedgering(*flipface, CCW); // 'flipface' is bae.
  fnext(*flipface, abce);
  esymself(abce);
  adjustedgering(*flipface, CW); // 'flipface' is abe.
  fnext(*flipface, bade);
#ifdef SELF_CHECK
  //assert(bade.tet != dummytet);
#endif
  esymself(bade);
  pa = org(abce);
  pb = dest(abce);
  pc = apex(abce);
  pd = apex(bade);
  pe = oppo(bade);
#ifdef SELF_CHECK
  //assert(oppo(abce) == pe);
#endif
  sym(abce, bacf);
  mirrorflag = bacf.tet != dummytet;
  if (mirrorflag) {
    // findedge(&bacf, pb, pa);
    bacf.ver = 0;
    for (i = 0; (i < 3) && (org(bacf) != pb); i++) {
      enextself(bacf);
    }
    sym(bade, abdf);
#ifdef SELF_CHECK
    //assert(abdf.tet != dummytet);
#endif
    // findedge(&abdf, pa, pb);
    abdf.ver = 0;
    for (i = 0; (i < 3) && (org(abdf) != pa); i++) {
      enextself(abdf);
    }
    pf = oppo(bacf);
#ifdef SELF_CHECK
    //assert(oppo(abdf) == pf);
#endif
  } 

  if (b->verbose > 1) {
    //printf("    Flip edge (%d, %d) to (%d, %d) %s.\n", pointmark(pa), 
      //pointmark(pb), pointmark(pc), pointmark(pd), mirrorflag ? "T44" : "T22");
  }
  mirrorflag ? flip44s++ : flip22s++;

  // Save the old configuration at the convex hull.
  enextfnext(abce, oldbce);
  enext2fnext(abce, oldcae);
  enextfnext(bade, oldade);
  enext2fnext(bade, olddbe);
  sym(oldbce, bcecasing);
  sym(oldcae, caecasing);
  sym(oldade, adecasing);
  sym(olddbe, dbecasing);
  if (checksubfaces) {
    tspivot(oldbce, bcesh);
    tspivot(oldcae, caesh);
    tspivot(oldade, adesh);
    tspivot(olddbe, dbesh);
    tspivot(abce, abc);
    tspivot(bade, bad);
  } 
  if (checksubsegs) {
    // Coplanar segs: a->d->b->c.
    enext(bade, worktet); 
    tsspivot1(worktet, adseg);
    enext2(bade, worktet);
    tsspivot1(worktet, dbseg);
    enext(abce, worktet);
    tsspivot1(worktet, bcseg);
    enext2(abce, worktet);
    tsspivot1(worktet, caseg);
    // Above segs: a->e, d->e, b->e, c->e.
    fnext(bade, worktet);
    enextself(worktet);
    tsspivot1(worktet, aeseg);
    enextfnext(bade, worktet);
    enextself(worktet);
    tsspivot1(worktet, deseg);
    enext2fnext(bade, worktet);
    enextself(worktet);
    tsspivot1(worktet, beseg);
    enextfnext(abce, worktet);
    enextself(worktet);
    tsspivot1(worktet, ceseg);
  }
  if (mirrorflag) {
    enextfnext(bacf, oldacf);
    enext2fnext(bacf, oldcbf);
    enextfnext(abdf, oldbdf);
    enext2fnext(abdf, olddaf);
    sym(oldacf, acfcasing);
    sym(oldcbf, cbfcasing);
    sym(oldbdf, bdfcasing);
    sym(olddaf, dafcasing);
    if (checksubfaces) {
      tspivot(oldacf, acfsh);
      tspivot(oldcbf, cbfsh);
      tspivot(oldbdf, bdfsh);
      tspivot(olddaf, dafsh);
    } 
    if (checksubsegs) {
      // Below segs: a->f, d->f, b->f, c->f.
      fnext(abdf, worktet);
      enext2self(worktet);
      tsspivot1(worktet, afseg);
      enext2fnext(abdf, worktet);
      enext2self(worktet);
      tsspivot1(worktet, dfseg);
      enextfnext(abdf, worktet);
      enext2self(worktet);
      tsspivot1(worktet, bfseg);
      enextfnext(bacf, worktet);
      enextself(worktet);
      tsspivot1(worktet, cfseg);
    }
  }

  // Rotate abce, bade one-quarter turn counterclockwise.
  bond(oldbce, caecasing);
  bond(oldcae, adecasing);
  bond(oldade, dbecasing);
  bond(olddbe, bcecasing);
  if (checksubfaces) {
    // Check for subfaces and rebond them to the rotated tets.
    if (caesh.sh == dummysh) {
      tsdissolve(oldbce);
    } else {
      tsbond(oldbce, caesh);
    }
    if (adesh.sh == dummysh) {
      tsdissolve(oldcae);
    } else {
      tsbond(oldcae, adesh);
    }
    if (dbesh.sh == dummysh) {
      tsdissolve(oldade);
    } else {
      tsbond(oldade, dbesh);
    }
    if (bcesh.sh == dummysh) {
      tsdissolve(olddbe);
    } else {
      tsbond(olddbe, bcesh);
    }
  } 
  if (checksubsegs) {
    // 5 edges in abce are changed.
    enext(abce, worktet);  // fit b->c into c->a.
    if (caseg.sh == dummysh) {
      tssdissolve1(worktet);
    } else {
      tssbond1(worktet, caseg);
    }
    enext2(abce, worktet); // fit c->a into a->d.
    if (adseg.sh == dummysh) {
      tssdissolve1(worktet);
    } else {
      tssbond1(worktet, adseg);
    }
    fnext(abce, worktet); // fit b->e into c->e.
    enextself(worktet);
    if (ceseg.sh == dummysh) {
      tssdissolve1(worktet);
    } else {
      tssbond1(worktet, ceseg);
    }
    enextfnext(abce, worktet); // fit c->e into a->e.
    enextself(worktet);
    if (aeseg.sh == dummysh) {
      tssdissolve1(worktet);
    } else {
      tssbond1(worktet, aeseg);
    }
    enext2fnext(abce, worktet); // fit a->e into d->e.
    enextself(worktet);
    if (deseg.sh == dummysh) {
      tssdissolve1(worktet);
    } else {
      tssbond1(worktet, deseg);
    }
    // 5 edges in bade are changed.
    enext(bade, worktet); // fit a->d into d->b.
    if (dbseg.sh == dummysh) {
      tssdissolve1(worktet);
    } else {
      tssbond1(worktet, dbseg);
    }
    enext2(bade, worktet); // fit d->b into b->c.
    if (bcseg.sh == dummysh) {
      tssdissolve1(worktet);
    } else {
      tssbond1(worktet, bcseg);
    }
    fnext(bade, worktet); // fit a->e into d->e.
    enextself(worktet);
    if (deseg.sh == dummysh) {
      tssdissolve1(worktet);
    } else {
      tssbond1(worktet, deseg);
    }
    enextfnext(bade, worktet); // fit d->e into b->e.
    enextself(worktet);
    if (beseg.sh == dummysh) {
      tssdissolve1(worktet);
    } else {
      tssbond1(worktet, beseg);
    }
    enext2fnext(bade, worktet); // fit b->e into c->e.
    enextself(worktet);
    if (ceseg.sh == dummysh) {
      tssdissolve1(worktet);
    } else {
      tssbond1(worktet, ceseg);
    }
  }
  if (mirrorflag) {
    // Rotate bacf, abdf one-quarter turn counterclockwise.
    bond(oldcbf, acfcasing);
    bond(oldacf, dafcasing);
    bond(olddaf, bdfcasing);
    bond(oldbdf, cbfcasing);
    if (checksubfaces) {
      // Check for subfaces and rebond them to the rotated tets.
      if (acfsh.sh == dummysh) {
        tsdissolve(oldcbf);
      } else {
        tsbond(oldcbf, acfsh);
      }
      if (dafsh.sh == dummysh) {
        tsdissolve(oldacf);
      } else {
        tsbond(oldacf, dafsh);
      }
      if (bdfsh.sh == dummysh) {
        tsdissolve(olddaf);
      } else {
        tsbond(olddaf, bdfsh);
      }
      if (cbfsh.sh == dummysh) {
        tsdissolve(oldbdf);
      } else {
        tsbond(oldbdf, cbfsh);
      }
    } 
    if (checksubsegs) {
      // 5 edges in bacf are changed.
      enext2(bacf, worktet); // fit b->c into c->a.
      if (caseg.sh == dummysh) {
        tssdissolve1(worktet);
      } else {
        tssbond1(worktet, caseg);
      }
      enext(bacf, worktet); // fit c->a into a->d.
      if (adseg.sh == dummysh) {
        tssdissolve1(worktet);
      } else {
        tssbond1(worktet, adseg);
      }
      fnext(bacf, worktet); // fit b->f into c->f.
      enext2self(worktet);
      if (cfseg.sh == dummysh) {
        tssdissolve1(worktet);
      } else {
        tssbond1(worktet, cfseg);
      }
      enext2fnext(bacf, worktet); // fit c->f into a->f.
      enext2self(worktet);
      if (afseg.sh == dummysh) {
        tssdissolve1(worktet);
      } else {
        tssbond1(worktet, afseg);
      }
      enextfnext(bacf, worktet); // fit a->f into d->f.
      enext2self(worktet);
      if (dfseg.sh == dummysh) {
        tssdissolve1(worktet);
      } else {
        tssbond1(worktet, dfseg);
      }
      // 5 edges in abdf are changed.
      enext2(abdf, worktet); // fit a->d into d->b.
      if (dbseg.sh == dummysh) {
        tssdissolve1(worktet);
      } else {
        tssbond1(worktet, dbseg);
      }
      enext(abdf, worktet); // fit d->b into b->c.
      if (bcseg.sh == dummysh) {
        tssdissolve1(worktet);
      } else {
        tssbond1(worktet, bcseg);
      }
      fnext(abdf, worktet); // fit a->f into d->f.
      enext2self(worktet);
      if (dfseg.sh == dummysh) {
        tssdissolve1(worktet);
      } else {
        tssbond1(worktet, dfseg);
      }
      enext2fnext(abdf, worktet); // fit d->f into b->f.
      enext2self(worktet);
      if (bfseg.sh == dummysh) {
        tssdissolve1(worktet);
      } else {
        tssbond1(worktet, bfseg);
      }
      enextfnext(abdf, worktet); // fit b->f into c->f.
      enext2self(worktet);
      if (cfseg.sh == dummysh) {
        tssdissolve1(worktet);
      } else {
        tssbond1(worktet, cfseg);
      }
    }
  }

  // New vertex assignments for the rotated tetrahedra.
  setorg(abce, pd); // Update abce to dcae
  setdest(abce, pc);
  setapex(abce, pa);
  setorg(bade, pc); // Update bade to cdbe
  setdest(bade, pd);
  setapex(bade, pb);
  if (mirrorflag) {
    setorg(bacf, pc); // Update bacf to cdaf
    setdest(bacf, pd);
    setapex(bacf, pa);
    setorg(abdf, pd); // Update abdf to dcbf
    setdest(abdf, pc);
    setapex(abdf, pb);
  }

  // Update point-to-tet map.
  setpoint2tet(pa, encode(abce));
  setpoint2tet(pb, encode(bade));
  setpoint2tet(pc, encode(abce));
  setpoint2tet(pd, encode(bade));
  setpoint2tet(pe, encode(abce));
  if (mirrorflag) {
    setpoint2tet(pf, encode(bacf));
  }

  // Are there subfaces need to be flipped?
  if (checksubfaces && abc.sh != dummysh) {
#ifdef SELF_CHECK
    //assert(bad.sh != dummysh);
#endif
    // Adjust the edge be ab, so the rotation of subfaces is according with
    //   the rotation of tetrahedra.
    findedge(&abc, pa, pb);
    // Flip an edge of two subfaces, ignore non-Delaunay edges.
    flip22sub(&abc, NULL);
  }

  if (b->verbose > 3) {
    //printf("    Updating abce ");
    printtet(&abce);
    //printf("    Updating bade ");
    printtet(&bade);
    if (mirrorflag) {
      //printf("    Updating bacf ");
      printtet(&bacf);
      //printf("    Updating abdf ");
      printtet(&abdf);
    }
  }

  if (flipqueue != (queue *) NULL) { 
    enextfnext(abce, bcecasing);
    enqueueflipface(bcecasing, flipqueue);
    enext2fnext(abce, caecasing);
    enqueueflipface(caecasing, flipqueue);
    enextfnext(bade, adecasing);
    enqueueflipface(adecasing, flipqueue);
    enext2fnext(bade, dbecasing);
    enqueueflipface(dbecasing, flipqueue);
    if (mirrorflag) {
      enextfnext(bacf, acfcasing);
      enqueueflipface(acfcasing, flipqueue);
      enext2fnext(bacf, cbfcasing);
      enqueueflipface(cbfcasing, flipqueue);
      enextfnext(abdf, bdfcasing);
      enqueueflipface(bdfcasing, flipqueue);
      enext2fnext(abdf, dafcasing);
      enqueueflipface(dafcasing, flipqueue);
    }
    // The two new faces dcae (abce), cdbe (bade) may still not be locally
    //   Delaunay, and may need be flipped (flip23).  On the other hand, in
    //   conforming Delaunay algorithm, two new subfaces dca (abc), and cdb
    //   (bad) may be non-conforming Delaunay, they need be queued if they
    //   are locally Delaunay but non-conforming Delaunay.
    enqueueflipface(abce, flipqueue);
    enqueueflipface(bade, flipqueue);
  }

  // Save a live handle in 'recenttet'.
  recenttet = abce;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// flip22sub()    Perform a 2-to-2 flip on a subface edge.                   //
//                                                                           //
// The flip edge is given by subface 'flipedge'.  Let it is abc, where ab is //
// the flipping edge.  The other subface is bad,  where a, b, c, d form a    //
// convex quadrilateral.  ab is not a subsegment.                            //
//                                                                           //
// A 2-to-2 subface flip is to change two subfaces abc and bad to another    //
// two subfaces dca and cdb.  Hence, edge ab has been removed and dc becomes //
// an edge. If a point e is above abc, this flip is equal to rotate abc and  //
// bad counterclockwise using right-hand rule with thumb points to e. It is  //
// important to know that the edge rings of the flipped subfaces dca and cdb //
// are keeping the same orientation as their original subfaces. So they have //
// the same orientation with respect to the lift point of this facet.        //
//                                                                           //
// During rotating, the face rings of the four edges bc, ca, ad, and de need //
// be re-connected. If the edge is not a subsegment, then its face ring has  //
// only two faces, a sbond() will bond them together. If it is a subsegment, //
// one should use sbond1() twice to bond two different handles to the rotat- //
// ing subface, one is predecssor (-casin), another is successor (-casout).  //
//                                                                           //
// If 'flipqueue' is not NULL, it returns four edges bc, ca, ad, de, which   //
// may be non-Delaunay.                                                      //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::flip22sub(face* flipedge, queue* flipqueue)
{
  face abc, bad;
  face oldbc, oldca, oldad, olddb;
  face bccasin, bccasout, cacasin, cacasout;
  face adcasin, adcasout, dbcasin, dbcasout;
  face bc, ca, ad, db;
  face spinsh;
  point pa, pb, pc, pd;

  abc = *flipedge;
  spivot(abc, bad);
  if (sorg(bad) != sdest(abc)) {
    sesymself(bad);
  }
  pa = sorg(abc);
  pb = sdest(abc);
  pc = sapex(abc);
  pd = sapex(bad);

  if (b->verbose > 1) {
    //printf("    Flip subedge (%d, %d) to (%d, %d).\n", pointmark(pa), 
      //pointmark(pb), pointmark(pc), pointmark(pd));
  }

  // Unmark the flipped subfaces (used in mesh refinement). 2009-08-17.
  sunmarktest(abc);
  sunmarktest(bad);

  // Save the old configuration outside the quadrilateral.  
  senext(abc, oldbc);
  senext2(abc, oldca);
  senext(bad, oldad);
  senext2(bad, olddb);
  // Get the outside connection. Becareful if there is a subsegment on the
  //   quadrilateral, two casings (casin and casout) are needed to save for
  //   keeping the face link.
  spivot(oldbc, bccasout);
  sspivot(oldbc, bc);
  if (bc.sh != dummysh) {
    // 'bc' is a subsegment.
    if (bccasout.sh != dummysh) {
      if (oldbc.sh != bccasout.sh) {
        // 'oldbc' is not self-bonded.
        spinsh = bccasout;
        do {
          bccasin = spinsh;
          spivotself(spinsh);
        } while (spinsh.sh != oldbc.sh);
      } else {
        bccasout.sh = dummysh;
      }
    }
    ssdissolve(oldbc);
  }
  spivot(oldca, cacasout);
  sspivot(oldca, ca);
  if (ca.sh != dummysh) {
    // 'ca' is a subsegment.
    if (cacasout.sh != dummysh) {
      if (oldca.sh != cacasout.sh) {
        // 'oldca' is not self-bonded.
        spinsh = cacasout;
        do {
          cacasin = spinsh;
          spivotself(spinsh);
        } while (spinsh.sh != oldca.sh);
      } else {
        cacasout.sh = dummysh;
      }
    }
    ssdissolve(oldca);
  }
  spivot(oldad, adcasout);
  sspivot(oldad, ad);
  if (ad.sh != dummysh) {
    // 'ad' is a subsegment. 
    if (adcasout.sh != dummysh) {
      if (oldad.sh != adcasout.sh) {
        // 'adcasout' is not self-bonded.
        spinsh = adcasout;
        do {
          adcasin = spinsh;
          spivotself(spinsh);
        } while (spinsh.sh != oldad.sh);
      } else {
        adcasout.sh = dummysh;
      }
    }
    ssdissolve(oldad);
  }
  spivot(olddb, dbcasout);
  sspivot(olddb, db);
  if (db.sh != dummysh) {
    // 'db' is a subsegment.
    if (dbcasout.sh != dummysh) {
      if (olddb.sh != dbcasout.sh) {
        // 'dbcasout' is not self-bonded.
        spinsh = dbcasout;
        do {
          dbcasin = spinsh;
          spivotself(spinsh);
        } while (spinsh.sh != olddb.sh);
      } else {
        dbcasout.sh = dummysh;
      }
    }
    ssdissolve(olddb);
  }

  // Rotate abc and bad one-quarter turn counterclockwise.
  if (ca.sh != dummysh) {
    if (cacasout.sh != dummysh) {
      sbond1(cacasin, oldbc);
      sbond1(oldbc, cacasout);
    } else {
      // Bond 'oldbc' to itself.
      sdissolve(oldbc); // sbond(oldbc, oldbc);
      // Make sure that dummysh always correctly bonded.
      dummysh[0] = sencode(oldbc);
    }
    ssbond(oldbc, ca);
  } else {
    sbond(oldbc, cacasout);
  }
  if (ad.sh != dummysh) {
    if (adcasout.sh != dummysh) {
      sbond1(adcasin, oldca);
      sbond1(oldca, adcasout);
    } else {
      // Bond 'oldca' to itself.
      sdissolve(oldca); // sbond(oldca, oldca);
      // Make sure that dummysh always correctly bonded.
      dummysh[0] = sencode(oldca);
    }
    ssbond(oldca, ad);
  } else {
    sbond(oldca, adcasout);
  }
  if (db.sh != dummysh) {
    if (dbcasout.sh != dummysh) {
      sbond1(dbcasin, oldad);
      sbond1(oldad, dbcasout);
    } else {
      // Bond 'oldad' to itself.
      sdissolve(oldad); // sbond(oldad, oldad);
      // Make sure that dummysh always correctly bonded.
      dummysh[0] = sencode(oldad);
    }
    ssbond(oldad, db);
  } else {
    sbond(oldad, dbcasout);
  }
  if (bc.sh != dummysh) {
    if (bccasout.sh != dummysh) {
      sbond1(bccasin, olddb);
      sbond1(olddb, bccasout);
    } else {
      // Bond 'olddb' to itself.
      sdissolve(olddb); // sbond(olddb, olddb);
      // Make sure that dummysh always correctly bonded.
      dummysh[0] = sencode(olddb);
    }
    ssbond(olddb, bc);
  } else {
    sbond(olddb, bccasout);
  }

  // New vertex assignments for the rotated subfaces.
  setsorg(abc, pd);  // Update abc to dca.
  setsdest(abc, pc);
  setsapex(abc, pa);
  setsorg(bad, pc);  // Update bad to cdb.
  setsdest(bad, pd);
  setsapex(bad, pb);

  // Update the point-to-subface map.
  // Comemnt: After the flip, abc becomes dca, bad becodes cdb. 
  setpoint2sh(pa, sencode(abc)); // dca
  setpoint2sh(pb, sencode(bad)); // cdb
  setpoint2sh(pc, sencode(bad));
  setpoint2sh(pd, sencode(bad));

  if (flipqueue != (queue *) NULL) {
    enqueueflipedge(bccasout, flipqueue);
    enqueueflipedge(cacasout, flipqueue);
    enqueueflipedge(adcasout, flipqueue);
    enqueueflipedge(dbcasout, flipqueue);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// lawson3d()    Perform 3D Lawson flips on non-Delaunay faces/edges.        //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

long tetgenmesh::lawson3d(queue* flipqueue)
{
  badface *qface;
  triface flipface, symface, flipedge;
  triface neighface, symneighface;
  face checksh, checkseg;
  face neighsh, symneighsh;
  point pa, pb, pc, pd, pe;
  point end1, end2;
  REAL sign, ori1, ori2, ori3;
  REAL ori4, len, vol;
  long flipcount;
  int copflag;
  int i;

  if (b->verbose > 1) {
    //printf("    Lawson flip: %ld faces.\n", flipqueue->len());
  }
  flipcount = flip23s + flip32s + flip22s + flip44s;

  // Loop until the queue is empty.
  while (!flipqueue->empty()) {
    qface = (badface *) flipqueue->pop();
    flipface = qface->tt;
    if (isdead(&flipface)) continue;
    if (flipface.tet == dummytet) continue;
    // Do not flip it if it is a subface.
    tspivot(flipface, checksh);
    if (checksh.sh != dummysh) continue;

    sym(flipface, symface);
    // Only do check when the adjacent tet exists and it's not a "fake" tet.
    if ((symface.tet != dummytet) && (oppo(symface) == qface->foppo)) {
      flipface.ver = 0; // CCW.
      pa = org(flipface);
      pb = dest(flipface);
      pc = apex(flipface);
      pd = oppo(flipface);
      pe = oppo(symface);
      sign = insphere_s(pb, pa, pc, pd, pe);
      //assert(sign != 0.0);

      if (sign > 0.0) {
        // flipface is not locally Delaunay. Try to flip it.
        ori1 = orient3d(pa, pb, pd, pe);
        ori2 = orient3d(pb, pc, pd, pe);
        ori3 = orient3d(pc, pa, pd, pe);

        flipedge = flipface;  // Initialize flipedge.
        copflag = 0;

        // Find a suitable flip.
        if (ori1 > 0) {
          if (ori2 > 0) {
            if (ori3 > 0) { // (+++)
              // A 2-to-3 flip is found.
              // Do not flip it if it is a subface.
              // tspivot(flipface, checksh);
              // if (checksh.sh == dummysh) {
                // Do not flip it if it will create a tet spanning two
                //   "coplanar" subfaces. We treat this case as either
                //   a 2-to-2 or a 4-to-4 flip. 
                for (i = 0; i < 3; i++) {
                  tsspivot(&flipface, &checkseg);
                  if (checkseg.sh == dummysh) {
                    fnext(flipface, neighface);
                    tspivot(neighface, neighsh);
                    if (neighsh.sh != dummysh) {
                      // Check if there exist another subface.
                      symedge(flipface, symface);
                      fnext(symface, symneighface);
                      tspivot(symneighface, symneighsh);
                      if (symneighsh.sh != dummysh) {
                        // Do not flip this face. Try to do a 2-to-2 or a
                        //   4-to-4 flip instead.
                        flipedge = flipface;
                        copflag = 1;
                        break; 
                      }
                    }
                  }
                  enextself(flipface);
                }
                if (i == 3) {
                  // Do not flip if it will create a nearly degenerate tet
                  //   at a segment. Once we created such a tet, it may
                  //   prevent you to split the segment later. An example
                  //   is in dump-.lua
                  for (i = 0; i < 3; i++) {
                    tsspivot(&flipface, &checkseg);
                    if (checkseg.sh != dummysh) {
                      end1 = (point) checkseg.sh[3];
                      end2 = (point) checkseg.sh[4];
                      ori4 = orient3d(end1, end2, pd, pe);
                      len = distance(end1, end2);
                      vol = len * len * len;
                      // Is it nearly degnerate?
                      if ((fabs(ori4) / vol) < b->epsilon) {
                        flipedge = flipface;
                        copflag = 0;
                        break;
                      }
                    }
                    enextself(flipface);
                  }
                  if (i == 3) {
                    flip23(&flipface, flipqueue);
                    continue;
                  }
                }
              // }
            } else {
              if (ori3 < 0) { // (++-) 
                // Try to flip edge [c, a].
                flipedge.ver = 4;
                copflag = 0;
              } else { // (++0)
                // A 2-to-2 or 4-to-4 flip at edge [c, a].
                flipedge.ver = 4;
                copflag = 1;
              }
            }
          } else {
            if (ori2 < 0) {
              if (ori3 > 0) { // (+-+)
                // Try to flip edge [b, c].
                flipedge.ver = 2;
                copflag = 0;
              } else {
                if (ori3 < 0) { // (+--)
                  // Not possible when pe is inside the circumsphere of 
                  //   the tet [pa.pb, pc, pd].
                  //assert(0);
                } else { // (+-0)
                  //assert(0); // The same reason as above.
                }
              }
            } else { // ori2 == 0
              if (ori3 > 0) { // (+0+)
                // A 2-to-2 or 4-to-4 flip at edge [b, c].
                flipedge.ver = 2;
                copflag = 1;
              } else {
                if (ori3 < 0) { // (+0-)
                  // Not possible when pe is inside the circumsphere of 
                  //   the tet [pa.pb, pc, pd].
                  //assert(0);
                } else { // (+00)
                  //assert(0); // The same reason as above.
                }
              }
            }
          }
        } else {
          if (ori1 < 0) {
            if (ori2 > 0) {
              if (ori3 > 0) { // (-++)
                // Try to flip edge [a, b].
                flipedge.ver = 0;
                copflag = 0;
              } else {
                if (ori3 < 0) { // (-+-)
                  // Not possible when pe is inside the circumsphere of 
                  //   the tet [pa.pb, pc, pd].
                  //assert(0);
                } else { // (-+0)
                  //assert(0); // The same reason as above.
                }
              }
            } else {
              if (ori2 < 0) {
                if (ori3 > 0) { // (--+)
                  // Not possible when pe is inside the circumsphere of 
                  //   the tet [pa.pb, pc, pd].
                  //assert(0);
                } else {
                  if (ori3 < 0) { // (---)
                    //assert(0);
                  } else { // (--0)
                    //assert(0);
                  }
                }
              } else { // ori2 == 0
                if (ori3 > 0) { // (-0+)
                  //assert(0);
                } else {
                  if (ori3 < 0) { // (-0-)
                    //assert(0);
                  } else { // (-00)
                    //assert(0);
                  }
                }
              }
            }
          } else { // ori1 == 0
            if (ori2 > 0) {
              if (ori3 > 0) { // (0++)
                // A 2-to-2 or 4-to-4 flip at edge [a, b].
                flipedge.ver = 0;
                copflag = 1;
              } else {
                if (ori3 < 0) { // (0+-)
                  //assert(0);
                } else { // (0+0)
                  //assert(0);
                }
              }
            } else {
              if (ori2 < 0) {
                if (ori3 > 0) { // (0-+)
                  //assert(0);
                } else {
                  if (ori3 < 0) { // (0--)
                    //assert(0);
                  } else { // (0-0)
                    //assert(0);
                  }
                }
              } else {
                if (ori3 > 0) { // (00+)
                  //assert(0);
                } else {
                  if (ori3 < 0) { // (00-)
                    //assert(0);
                  } else { // (000)
                    //assert(0);
                  }
                }
              }
            }
          }
        }

        // An edge (flipedge) is going to be flipped.
        // Do not flip it it is a subsegment.
        tsspivot(&flipedge, &checkseg);
        if (checkseg.sh == dummysh) {
          symedge(flipedge, symface);
          if (copflag == 0) {
            // Check if a 3-to-2 flip is possible.
            tfnext(flipedge, neighface);
            if (neighface.tet != dummytet) {
              // Check if neighface is a subface.
              tspivot(neighface, neighsh);
              if (neighsh.sh == dummysh) {
                tfnext(symface, symneighface);
                if (neighface.tet == symneighface.tet) {
                  // symneighface should not be a subface. Check it.
                  tspivot(symneighface, symneighsh);
                  //assert(symneighsh.sh == dummysh);
                  // Found a 3-to-2 flip.
                  flip32(&flipedge, flipqueue);
                }
              } else {
                // neighsh is a subface. Check a potential 4-to-4 flip. 
                tfnext(symface, symneighface);
                tspivot(symneighface, symneighsh);
                if (symneighsh.sh != dummysh) {
                  if (oppo(neighface) == oppo(symneighface)) {
                    // Found a 4-to-4 flip.
                    flip22(&flipedge, flipqueue);
                  }
                }
              }
            } else {
              // neightface is a hull face. Since flipedge is not a segment
              //   and this edge is locally non-convex.
              tfnext(symface, symneighface); 
              // symneighface should also be a hull face.
              if (symneighface.tet == dummytet) {
                // Force a 2-to-2 flip (recovery of Delaunay).
                flip22(&flipedge, flipqueue);
              }
            }
          } else {
            // Check if a 2-to-2 or 4-to-4 flip is possible.
            tfnext(flipedge, neighface);
            tfnext(symface, symneighface);
            if (neighface.tet != dummytet) {
              if (symneighface.tet != dummytet) {
                if (oppo(neighface) == oppo(symneighface)) {
                  // Found a 4-to-4 flip.
                  flip22(&flipedge, flipqueue);
                }
              }
            } else {
              if (symneighface.tet == dummytet) {
                // Found a 2-to-2 flip.
                flip22(&flipedge, flipqueue);
              }
            }
          }
        }

      } // if (sign > 0)
    }
  } // while (!flipqueue->empty())

  flipcount = flip23s + flip32s + flip22s + flip44s - flipcount;
  if (b->verbose > 1) {
    //printf("    %ld flips.\n", flipcount);
  }

  return flipcount;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// lawson()    Perform lawson flips on non-Delaunay edges.                   //
//                                                                           //
// Assumpation:  Current triangulation T contains non-Delaunay edges (after  //
// inserting a point or performing a flip). Non-Delaunay edges are queued in //
// 'facequeue'. Returns the total number of flips done during this call.     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

long tetgenmesh::lawson(queue* flipqueue)
{
  badface *qedge;
  face flipedge, symedge;
  face checkseg;
  point pa, pb, pc, pd;
  REAL vab[3], vac[3], vad[3];
  REAL dot1, dot2, lac, lad; 
  REAL sign, ori;
  int edgeflips, maxflips;
  int i;

  if (b->verbose > 1) {
    //printf("    Lawson flip: %ld edges.\n", flipqueue->len());
  }

  if (b->diagnose) {
    maxflips = (int) ((flipqueue->len() + 1l) * 3l);
    maxflips *= maxflips;
  } else {
    maxflips = -1;
  }
  edgeflips = 0;

  while (!flipqueue->empty() && maxflips != 0) {
    qedge = (badface *) flipqueue->pop();
    flipedge = qedge->ss;
    if (flipedge.sh == dummysh) continue;
    if ((sorg(flipedge) != qedge->forg) || 
        (sdest(flipedge) != qedge->fdest)) continue; 
    sspivot(flipedge, checkseg);
    if (checkseg.sh != dummysh) continue;  // Can't flip a subsegment.
    spivot(flipedge, symedge);
    if (symedge.sh == dummysh) continue; // Can't flip a hull edge.
    pa = sorg(flipedge);
    pb = sdest(flipedge);
    pc = sapex(flipedge);
    pd = sapex(symedge);
    // Choose the triangle abc or abd as the base depending on the angle1
    //   (Vac, Vab) and angle2 (Vad, Vab).
    for (i = 0; i < 3; i++) vab[i] = pb[i] - pa[i];
    for (i = 0; i < 3; i++) vac[i] = pc[i] - pa[i];
    for (i = 0; i < 3; i++) vad[i] = pd[i] - pa[i];
    dot1 = dot(vac, vab);
    dot2 = dot(vad, vab);
    dot1 *= dot1;
    dot2 *= dot2;
    lac = dot(vac, vac);
    lad = dot(vad, vad);
    if (lad * dot1 <= lac * dot2) {
      // angle1 is closer to 90 than angle2, choose abc (flipedge).
      abovepoint = facetabovepointarray[shellmark(flipedge)];
      if (abovepoint == (point) NULL) {
        getfacetabovepoint(&flipedge);
      }
      sign = insphere(pa, pb, pc, abovepoint, pd);
      ori = orient3d(pa, pb, pc, abovepoint);
    } else {
      // angle2 is closer to 90 than angle1, choose abd (symedge).
      abovepoint = facetabovepointarray[shellmark(symedge)];
      if (abovepoint == (point) NULL) {
        getfacetabovepoint(&symedge);
      }
      sign = insphere(pa, pb, pd, abovepoint, pc);
      ori = orient3d(pa, pb, pd, abovepoint);
    }
    // Correct the sign.
    sign = ori > 0.0 ? sign : -sign;
    if (sign > 0.0) {
      // Flip the non-Delaunay edge.
      flip22sub(&flipedge, flipqueue);
      edgeflips++;
      if (maxflips > 0) maxflips--;
    }
  }

  if (!maxflips && !b->quiet) {
    //printf("Warning:  Maximal number of flips reached !\n");
  }

  if (b->verbose > 1) {
    //printf("  Total %d flips.\n", edgeflips);
  }

  return edgeflips;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// removetetbypeeloff()    Remove a boundary tet by peeling it off.          //
//                                                                           //
// 'striptet' (abcd) is on boundary and can be removed by stripping it off.  //
// Let abc and bad are the external boundary faces.                          //
//                                                                           //
// To strip 'abcd' from the mesh is to detach its two interal faces (dca and //
// cdb) from their adjoining tets together with a 2-to-2 flip to transform   //
// two subfaces (abc and bad) into another two (dca and cdb).                //
//                                                                           //
// 'adjtetlist[2]' returns the two new boundary faces (in tet) dca and cdb.  //
//                                                                           //
// In mesh optimization. It is possible that ab is a segment and abcd is a   //
// sliver on the hull. Strip abcd will also delete the segment ab.           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::removetetbypeeloff(triface *striptet, triface *adjtetlist)
{
  triface abcd, badc;
  triface dcacasing, cdbcasing;
  face abc, bad;
  face abseg;
  REAL ang;

  abcd = *striptet;
  adjustedgering(abcd, CCW);
  // Get the casing tets at the internal sides.
  enextfnext(abcd, cdbcasing);
  enext2fnext(abcd, dcacasing);
  symself(cdbcasing);
  symself(dcacasing);  
  // Do the neighboring tets exist?  During optimization. It is possible
  //   that the neighboring tets are already dead.
  if ((cdbcasing.tet == dummytet) || (dcacasing.tet == dummytet)) {
    // Do not strip this tet.
    return false;
  }

  // Are there subfaces?
  if (checksubfaces) {
    // Get the external subfaces abc, bad.
    fnext(abcd, badc);
    esymself(badc);
    tspivot(abcd, abc);
    tspivot(badc, bad);
    if (abc.sh != dummysh) { 
      //assert(bad.sh != dummysh);
      findedge(&abc, org(abcd), dest(abcd));
      findedge(&bad, org(badc), dest(badc));
      // Is ab a segment?
      sspivot(abc, abseg);
      if (abseg.sh != dummysh) {
        // Does a segment allow to be removed?
        if ((b->optlevel > 3) && (b->nobisect == 0)) {
          // Only remove this segment if the dihedal angle at ab is between
          //   [b->maxdihedral-9, 180] (deg).  This avoids mistakely fliping
          //   ab when it has actually no big dihedral angle while cd has.
          ang = facedihedral(org(abcd), dest(abcd), apex(abcd), oppo(abcd));
          ang = ang * 180.0 / PI;
          if ((ang + 9.0) > b->maxdihedral) {
            if (b->verbose > 1) {
              //printf("    Remove a segment during peeling.\n");
            }
            face prevseg, nextseg;
            // It is only shared by abc and bad (abcd is a tet).
            ssdissolve(abc);
            ssdissolve(bad);
            abseg.shver = 0;
            senext(abseg, nextseg);
            spivotself(nextseg);
            if (nextseg.sh != dummysh) {
              ssdissolve(nextseg);
            }
            senext2(abseg, prevseg);
            spivotself(prevseg);
            if (prevseg.sh != dummysh) {
              ssdissolve(prevseg);
            }
            shellfacedealloc(subsegs, abseg.sh);
            optcount[1]++;
          } else {
            return false;
          }
        } else {
          return false;
        }
      }
      // Do a 2-to-2 flip on abc and bad, transform abc->dca, bad->cdb.
      flip22sub(&abc, NULL);
      // The two internal faces become boundary faces.
      tsbond(cdbcasing, bad);
      tsbond(dcacasing, abc);
    }
  }

  // Detach abcd from the two internal faces.
  dissolve(cdbcasing);
  dissolve(dcacasing);
  // Delete abcd.
  tetrahedrondealloc(abcd.tet);

  adjtetlist[0] = cdbcasing;
  adjtetlist[1] = dcacasing;

  return true;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// removeedgebyflip22()    Remove an edge by a 2-to-2 (or 4-to-4) flip.      //
//                                                                           //
// 'abtetlist' contains n tets (n is 2 or 4) sharing edge ab,  abtetlist[0]  //
// and abtetlist[1] are tets abec and abde, respectively (NOTE, both are in  //
// CW edge ring), where a, b, c, and d are coplanar.  If n = 4, abtetlist[2] //
// and abtetlist[3] are tets abfd and abcf, respectively.  This routine uses //
// flip22() to replace edge ab with cd, the surrounding tets are rotated.    //
//                                                                           //
// If 'key' != NULL.  The old tets are replaced by the new tets only if the  //
// local mesh quality is improved. Current 'key' = cos(\theta), where \theta //
// is the maximum dihedral angle in the old tets.                            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::removeedgebyflip22(REAL *key, int n, triface *abtetlist,
  queue *flipque)
{
  point pa, pb, pc, pd, pe, pf;
  REAL cosmaxd, d1, d2, d3;
  bool doflip;

  doflip = true;
  adjustedgering(abtetlist[0], CW);
  pa = org(abtetlist[0]);
  pb = dest(abtetlist[0]);
  pe = apex(abtetlist[0]);
  pc = oppo(abtetlist[0]);
  pd = apex(abtetlist[1]);
  if (n == 4) {
    pf = apex(abtetlist[2]);
  }
  if (key && (*key > -1.0)) {
    tetalldihedral(pc, pd, pe, pa, NULL, &d1, NULL);
    tetalldihedral(pd, pc, pe, pb, NULL, &d2, NULL);
    cosmaxd = d1 < d2 ? d1 : d2; // Choose the bigger angle.
    if (n == 4) {
      tetalldihedral(pd, pc, pf, pa, NULL, &d1, NULL);
      tetalldihedral(pc, pd, pf, pb, NULL, &d2, NULL);
      d3 = d1 < d2 ? d1 : d2; // Choose the bigger angle.
      cosmaxd = cosmaxd < d3 ? cosmaxd : d3; // Choose the bigger angle.
    }
    doflip = (*key < cosmaxd); // Can local quality be improved?
  }

  if (doflip) {
    flip22(&abtetlist[0], NULL);
    // Return the improved quality value.
    if (key) *key = cosmaxd;
  }

  return doflip;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// removefacebyflip23()    Remove a face by a 2-to-3 flip.                   //
//                                                                           //
// 'abctetlist' contains 2 tets sharing abc, which are [0]abcd and [1]bace.  //
// This routine forms three new tets that abc is not a face anymore. Save    //
// them in 'newtetlist': [0]edab, [1]edbc, and [2]edca.  Note that the new   //
// tets may not valid if one of them get inverted. return false if so.       //
//                                                                           //
// If 'key' != NULL.  The old tets are replaced by the new tets only if the  //
// local mesh quality is improved. Current 'key' = cos(\theta), where \theta //
// is the maximum dihedral angle in the old tets.                            //
//                                                                           //
// If the face is flipped, 'newtetlist' returns the three new tets. The two  //
// tets in 'abctetlist' are NOT deleted.  The caller has the right to either //
// delete them or reverse the operation.                                     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::removefacebyflip23(REAL *key, triface *abctetlist,
  triface *newtetlist, queue *flipque)
{
  triface edab, edbc, edca; // new configuration.
  triface newfront, oldfront, adjfront;
  face checksh;
  point pa, pb, pc, pd, pe;
  REAL ori, cosmaxd, d1, d2, d3;
  REAL attrib, volume;
  bool doflip;
  int i;

  adjustedgering(abctetlist[0], CCW);
  pa = org(abctetlist[0]);
  pb = dest(abctetlist[0]);
  pc = apex(abctetlist[0]);
  pd = oppo(abctetlist[0]);  
  pe = oppo(abctetlist[1]);

  // Check if the flip creates valid new tets.
  ori = orient3d(pe, pd, pa, pb);
  if (ori < 0.0) {
    ori = orient3d(pe, pd, pb, pc);
    if (ori < 0.0) {
      ori = orient3d(pe, pd, pc, pa);
    }
  }
  doflip = (ori < 0.0); // Can abc be flipped away?
  if (doflip && (key != (REAL *) NULL)) {
    if (*key > -1.0) {
      // Test if the new tets reduce the maximal dihedral angle.
      tetalldihedral(pe, pd, pa, pb, NULL, &d1, NULL);
      tetalldihedral(pe, pd, pb, pc, NULL, &d2, NULL);
      tetalldihedral(pe, pd, pc, pa, NULL, &d3, NULL);
      cosmaxd = d1 < d2 ? d1 : d2; // Choose the bigger angle.
      cosmaxd = cosmaxd < d3 ? cosmaxd : d3; // Choose the bigger angle.
      doflip = (*key < cosmaxd); // Can local quality be improved?
    }
  }

  if (doflip) {
    // A valid (2-to-3) flip is found.
    flip23s++;
    // Create the new tets.
    maketetrahedron(&edab);
    setorg(edab, pe);
    setdest(edab, pd);
    setapex(edab, pa);
    setoppo(edab, pb);
    maketetrahedron(&edbc);
    setorg(edbc, pe);
    setdest(edbc, pd);
    setapex(edbc, pb);
    setoppo(edbc, pc);
    maketetrahedron(&edca);
    setorg(edca, pe);
    setdest(edca, pd);
    setapex(edca, pc);
    setoppo(edca, pa);
    // Transfer the element attributes.
    for (i = 0; i < in->numberoftetrahedronattributes; i++) {
      attrib = elemattribute(abctetlist[0].tet, i);
      setelemattribute(edab.tet, i, attrib);
      setelemattribute(edbc.tet, i, attrib);
      setelemattribute(edca.tet, i, attrib);
    }
    // Transfer the volume constraints.
    if (b->varvolume && !b->refine) {
      volume = volumebound(abctetlist[0].tet);
      setvolumebound(edab.tet, volume);
      setvolumebound(edbc.tet, volume);
      setvolumebound(edca.tet, volume);
    }
    // Return two new tets.
    newtetlist[0] = edab;
    newtetlist[1] = edbc;
    newtetlist[2] = edca;
    // Glue the three new tets.
    for (i = 0; i < 3; i++) {
      fnext(newtetlist[i], newfront);
      bond(newfront, newtetlist[(i + 1) % 3]);
    }
    // Substitute the three new tets into the old cavity.
    for (i = 0; i < 3; i++) {
      fnext(abctetlist[0], oldfront);
      sym(oldfront, adjfront); // may be outside.
      enextfnext(newtetlist[i], newfront);
      bond(newfront, adjfront);
      if (checksubfaces) {
        tspivot(oldfront, checksh);
        if (checksh.sh != dummysh) {
          tsbond(newfront, checksh);
        }
      }
      if (flipque != (queue *) NULL) {
        enqueueflipface(newfront, flipque);
      }
      enextself(abctetlist[0]);
    }
    findedge(&(abctetlist[1]), pb, pa);
    for (i = 0; i < 3; i++) {
      fnext(abctetlist[1], oldfront);
      sym(oldfront, adjfront); // may be outside.
      enext2fnext(newtetlist[i], newfront);
      bond(newfront, adjfront);
      if (checksubfaces) {
        tspivot(oldfront, checksh);
        if (checksh.sh != dummysh) {
          tsbond(newfront, checksh);
        }
      }
      if (flipque != (queue *) NULL) {
        enqueueflipface(newfront, flipque);
      }
      enext2self(abctetlist[1]);
    }
    // Do not delete the old tets.
    // for (i = 0; i < 2; i++) {
    //   tetrahedrondealloc(abctetlist[i].tet);
    // }
    // Return the improved quality value.
    if (key != (REAL *) NULL) *key = cosmaxd;
    return true;
  }

  return false;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// removeedgebyflip32()    Remove an edge by a 3-to-2 flip.                  //
//                                                                           //
// 'abtetlist' contains 3 tets sharing ab. Imaging that ab is perpendicular  //
// to the screen, where a lies in front of and b lies behind it. The 3 tets  //
// of the list are: [0]abce, [1]abdc, and [2]abed, respectively.             //
// Comment: the edge ab is in CW edge ring of the three faces: abc, abd, and //
// abe. (2009-06-29)                                                         //
//                                                                           //
// This routine forms two new tets that ab is not an edge of them. Save them //
// in 'newtetlist', [0]dcea, [1]cdeb. Note that the new tets may not valid   //
// if one of them get inverted. return false if so.                          //
//                                                                           //
// If 'key' != NULL.  The old tets are replaced by the new tets only if the  //
// local mesh quality is improved. Current 'key' = cos(\theta), where \theta //
// is the maximum dihedral angle in the old tets.                            //
//                                                                           //
// If the edge is flipped, 'newtetlist' returns the two new tets. The three  //
// tets in 'abtetlist' are NOT deleted.  The caller has the right to either  //
// delete them or reverse the operation.                                     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::removeedgebyflip32(REAL *key, triface *abtetlist,
  triface *newtetlist, queue *flipque)
{
  triface dcea, cdeb; // new configuration.
  triface newfront, oldfront, adjfront;
  face checksh, checkseg;
  point pa, pb, pc, pd, pe;
  REAL ori, cosmaxd, d1, d2;
  REAL attrib, volume;
  bool doflip;
  int i;

  pa = org(abtetlist[0]);
  pb = dest(abtetlist[0]);
  pc = apex(abtetlist[0]);
  pd = apex(abtetlist[1]);
  pe = apex(abtetlist[2]);

  ori = orient3d(pd, pc, pe, pa);
  if (ori < 0.0) {
    ori = orient3d(pc, pd, pe, pb);
  }
  doflip = (ori < 0.0); // Can ab be flipped away?

  // Does the caller ensure a valid configuration?
  if (doflip && (key != (REAL *) NULL)) {    
    if (*key > -1.0) {
      // Test if the new tets reduce the maximal dihedral angle.
      tetalldihedral(pd, pc, pe, pa, NULL, &d1, NULL);
      tetalldihedral(pc, pd, pe, pb, NULL, &d2, NULL);
      cosmaxd = d1 < d2 ? d1 : d2; // Choose the bigger angle.
      doflip = (*key < cosmaxd); // Can local quality be improved?
      // Return the key
      *key = cosmaxd;
    }
  }

  // Comment: This edge must not be fixed. It has been checked before.
  if (doflip && (elemfliplist != NULL)) {
    // Regist this flip.
    if (!registerelemflip(T32, pa, pb, dummypoint, pc, pd, pe)) {
      // Detected a potential flip loop. Don't do it.
      return false;
    }
  }

  if (doflip) {
    // Create the new tets.
    maketetrahedron(&dcea);
    setorg(dcea, pd);
    setdest(dcea, pc);
    setapex(dcea, pe);
    setoppo(dcea, pa);
    maketetrahedron(&cdeb);
    setorg(cdeb, pc);
    setdest(cdeb, pd);
    setapex(cdeb, pe);
    setoppo(cdeb, pb);
    // Transfer the element attributes.
    for (i = 0; i < in->numberoftetrahedronattributes; i++) {
      attrib = elemattribute(abtetlist[0].tet, i);
      setelemattribute(dcea.tet, i, attrib);
      setelemattribute(cdeb.tet, i, attrib);
    }
    // Transfer the volume constraints.
    if (b->varvolume && !b->refine) {
      volume = volumebound(abtetlist[0].tet);
      setvolumebound(dcea.tet, volume);
      setvolumebound(cdeb.tet, volume);
    }
    // Return two new tets.
    newtetlist[0] = dcea;
    newtetlist[1] = cdeb;
    // Glue the two new tets.
    bond(dcea, cdeb);
    // Substitute the two new tets into the old three-tets cavity.
    for (i = 0; i < 3; i++) {
      fnext(dcea, newfront); // face dca, cea, eda.
      esym(abtetlist[(i + 1) % 3], oldfront);
      enextfnextself(oldfront);
      // Get the adjacent tet at the face (may be a dummytet).
      sym(oldfront, adjfront);
      bond(newfront, adjfront);
      if (checksubfaces) {
        tspivot(oldfront, checksh);
        if (checksh.sh != dummysh) {
          tsbond(newfront, checksh);
        }
      }
      if (flipque != (queue *) NULL) {
        enqueueflipface(newfront, flipque);
      }
      enext2self(dcea);
    }
    for (i = 0; i < 3; i++) {
      fnext(cdeb, newfront); // face cdb, deb, ecb.
      esym(abtetlist[(i + 1) % 3], oldfront);
      enext2fnextself(oldfront);
      // Get the adjacent tet at the face (may be a dummytet).
      sym(oldfront, adjfront);
      bond(newfront, adjfront);
      if (checksubfaces) {
        tspivot(oldfront, checksh);
        if (checksh.sh != dummysh) {
          tsbond(newfront, checksh);
        }
      }
      if (flipque != (queue *) NULL) {
        enqueueflipface(newfront, flipque);
      }
      enextself(cdeb);
    }
    // Do not delete the old tets.
    // for (i = 0; i < 3; i++) {
    //   tetrahedrondealloc(abtetlist[i].tet);
    // }
    return true;
  } // if (doflip)

  return false;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// removeedgebytranNM()    Remove an edge by transforming n-to-m tets.       //
//                                                                           //
// This routine attempts to remove a given edge (ab) by transforming the set //
// T of tets surrounding ab into another set T' of tets.  T and T' have the  //
// same outer faces and ab is not an edge of T' anymore. Let |T|=n, and |T'| //
// =m, it is actually a n-to-m flip for n > 3.  The relation between n and m //
// depends on the method, ours is found below.                               //
//                                                                           //
// 'abtetlist' contains n tets sharing ab. Imaging that ab is perpendicular  //
// to the screen, where a lies in front of and b lies behind it.  Let the    //
// projections of the n apexes onto screen in clockwise order are: p_0, ...  //
// p_n-1, respectively. The tets in the list are: [0]abp_0p_n-1,[1]abp_1p_0, //
// ..., [n-1]abp_n-1p_n-2, respectively.                                     //
//                                                                           //
// The principle of the approach is: Recursively reduce the link of ab by    //
// using flip23 until only three faces remain, hence a flip32 can be applied //
// to remove ab. For a given face a.b.p_0, check a flip23 can be applied on  //
// it, i.e, edge p_1.p_n-1 crosses it. NOTE*** We do the flip even p_1.p_n-1 //
// intersects with a.b (they are coplanar). If so, a degenerate tet (a.b.p_1.//
// p_n-1) is temporarily created, but it will be eventually removed by the   //
// final flip32. This relaxation splits a flip44 into flip23 + flip32. *NOTE //
// Now suppose a.b.p_0 gets flipped, p_0 is not on the link of ab anymore.   //
// The link is then reduced (by 1). 2 of the 3 new tets, p_n-1.p_1.p_0.a and //
// p_1.p_n-1.p_0.b, will be part of the new configuration.  The left new tet,//
// a.b.p_1.p_n-1, goes into the new link of ab. A recurrence can be applied. //
//                                                                           //
// If 'e1' and 'e2' are not NULLs, they specify an wanted edge to appear in  //
// the new tet configuration. In such case, only do flip23 if edge e1<->e2   //
// can be recovered. It is used in removeedgebycombNM().                     //
//                                                                           //
// If ab gets removed. 'newtetlist' contains m new tets.  By using the above //
// approach, the pairs (n, m) can be easily enumerated.  For example, (3, 2),//
// (4, 4), (5, 6), (6, 8), (7, 10), (8, 12), (9, 14), (10, 16),  and so on.  //
// It is easy to deduce, that m = (n - 2) * 2, when n >= 3.  The n tets in   //
// 'abtetlist' are NOT deleted in this routine. The caller has the right to  //
// either delete them or reverse this operation.                             //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::removeedgebytranNM(REAL *key, int n, triface *abtetlist,
  triface *newtetlist, point e1, point e2, queue *flipque)
{
  triface tmpabtetlist[21]; // Temporary max 20 tets configuration.
  triface newfront, oldfront, adjfront;
  face checksh;
  point pa, pb, p[21];
  REAL ori, cosmaxd, d1, d2;
  REAL tmpkey;
  REAL attrib, volume;
  bool doflip, copflag, success;
  int i, j, k;

  // Maximum 20 tets.
  //assert(n < 20);  // n <= b->maxflipedgelinksize
  // Two points a and b are fixed.
  pa = org(abtetlist[0]);
  pb = dest(abtetlist[0]);
  // The points p_0, p_1, ..., p_n-1 are permuted in each new configuration.
  //   These permutations can be easily done in the following loop.
  // Loop through all the possible new tets configurations. Stop on finding
  //   a valid new tet configuration which also immproves the quality value.
  for (i = 0; i < n; i++) {
    // Get other n points for the current configuration.
    for (j = 0; j < n; j++) {
      p[j] = apex(abtetlist[(i + j) % n]);
    }
    // Is there a wanted edge?
    if ((e1 != (point) NULL) && (e2 != (point) NULL)) {
      // Yes. Skip this face if p[1]<->p[n-1] is not the edge.
      if (!(((p[1] == e1) && (p[n - 1] == e2)) ||
	    ((p[1] == e2) && (p[n - 1] == e1)))) continue;
    }
    // Test if face a.b.p_0 can be flipped (by flip23), ie, to check if the
    //   edge p_n-1.p_1 crosses face a.b.p_0 properly.
    // Note. It is possible that face a.b.p_0 has type flip44, ie, a,b,p_1,
    //   and p_n-1 are coplanar. A trick is to split the flip44 into two
    //   steps: frist a flip23, then a flip32. The first step creates a
    //   degenerate tet (vol=0) which will be removed by the second flip.
    ori = orient3d(pa, pb, p[1], p[n - 1]);
    copflag = (ori == 0.0); // Are they coplanar?
    if (ori >= 0.0) {
      // Accept the coplanar case which supports flip44.
      ori = orient3d(pb, p[0], p[1], p[n - 1]);
      if (ori > 0.0) {
        ori = orient3d(p[0], pa, p[1], p[n - 1]);
      }
    }
    // Is face abc flipable?
    if (ori > 0.0) {
      // A valid (2-to-3) flip (or 4-to-4 flip) is found.
      copflag ? flip44s++ : flip23s++;
      doflip = true;
      if (key != (REAL *) NULL) {
        if (*key > -1.0) {
          // Test if the new tets reduce the maximal dihedral angle. Only 2
          //   tets, p_n-1.p_1.p_0.a and p_1.p_n-1.p_0.b, need to be tested
          //   The left one a.b.p_n-1.p_1 goes into the new link of ab.
          tetalldihedral(p[n - 1], p[1], p[0], pa, NULL, &d1, NULL);
          tetalldihedral(p[1], p[n - 1], p[0], pb, NULL, &d2, NULL);
          cosmaxd = d1 < d2 ? d1 : d2; // Choose the bigger angle.
          doflip = *key < cosmaxd; // Can the local quality be improved?
        }
      }
      if (doflip && (elemfliplist != NULL)) {
        // Comment: The flipping face must be not fixed. This case has been
        //   tested during collecting the face ring of this edge.
        // Do not flip this face if it has been registered before.
        if (!registerelemflip(T23, pa, pb, p[0], p[1], p[n-1], dummypoint)) {
          doflip = false; // Do not flip this face.
        }
      }
      if (doflip) {
        tmpkey = key != NULL ? *key : -1.0;
        // Create the two new tets.
        maketetrahedron(&(newtetlist[0]));
        setorg(newtetlist[0], p[n - 1]);
        setdest(newtetlist[0], p[1]);
        setapex(newtetlist[0], p[0]);
        setoppo(newtetlist[0], pa);
        maketetrahedron(&(newtetlist[1]));
        setorg(newtetlist[1], p[1]);
        setdest(newtetlist[1], p[n - 1]);
        setapex(newtetlist[1], p[0]);
        setoppo(newtetlist[1], pb);
        // Create the n - 1 temporary new tets (the new Star(ab)).
        maketetrahedron(&(tmpabtetlist[0]));
        setorg(tmpabtetlist[0], pa);
        setdest(tmpabtetlist[0], pb);
        setapex(tmpabtetlist[0], p[n - 1]);
        setoppo(tmpabtetlist[0], p[1]);
        for (j = 1; j < n - 1; j++) {
          maketetrahedron(&(tmpabtetlist[j]));
          setorg(tmpabtetlist[j], pa);
          setdest(tmpabtetlist[j], pb);
          setapex(tmpabtetlist[j], p[j]);
          setoppo(tmpabtetlist[j], p[j + 1]);
        }
        // Transfer the element attributes.
        for (j = 0; j < in->numberoftetrahedronattributes; j++) {
          attrib = elemattribute(abtetlist[0].tet, j);
          setelemattribute(newtetlist[0].tet, j, attrib);
          setelemattribute(newtetlist[1].tet, j, attrib);
          for (k = 0; k < n - 1; k++) {
            setelemattribute(tmpabtetlist[k].tet, j, attrib);
          }
        }
        // Transfer the volume constraints.
        if (b->varvolume && !b->refine) {
          volume = volumebound(abtetlist[0].tet);
          setvolumebound(newtetlist[0].tet, volume);
          setvolumebound(newtetlist[1].tet, volume);
          for (k = 0; k < n - 1; k++) {
            setvolumebound(tmpabtetlist[k].tet, volume);
          }
        }
        // Glue the new tets at their internal faces: 2 + (n - 1).
        bond(newtetlist[0], newtetlist[1]); // p_n-1.p_1.p_0.
        fnext(newtetlist[0], newfront);
        enext2fnext(tmpabtetlist[0], adjfront);
        bond(newfront, adjfront); // p_n-1.p_1.a.
        fnext(newtetlist[1], newfront);
        enextfnext(tmpabtetlist[0], adjfront);
        bond(newfront, adjfront); // p_n-1.p_1.b.
        // Glue n - 1 internal faces around ab.
        for (j = 0; j < n - 1; j++) {
          fnext(tmpabtetlist[j], newfront);
          bond(newfront, tmpabtetlist[(j + 1) % (n - 1)]); // a.b.p_j+1
        }
        // Substitute the old tets with the new tets by connecting the new
        //   tets to the adjacent tets in the mesh. There are n * 2 (outer)
        //   faces of the new tets need to be operated.
        // Note, after the substitution, the old tets still have pointers to
        //   their adjacent tets in the mesh.  These pointers can be re-used
        //   to inverse the substitution.
        for (j = 0; j < n; j++) {
          // Get an old tet: [0]a.b.p_0.p_n-1 or [j]a.b.p_j.p_j-1, (j > 0).
          oldfront = abtetlist[(i + j) % n];
          esymself(oldfront);
          enextfnextself(oldfront);
          // Get an adjacent tet at face: [0]a.p_0.p_n-1 or [j]a.p_j.p_j-1.
          sym(oldfront, adjfront); // adjfront may be dummy.
          // Get the corresponding face from the new tets.
          if (j == 0) {
            enext2fnext(newtetlist[0], newfront); // a.p_0.n_n-1
          } else if (j == 1) {
            enextfnext(newtetlist[0], newfront); // a.p_1.p_0
          } else { // j >= 2.
            enext2fnext(tmpabtetlist[j - 1], newfront); // a.p_j.p_j-1
          }
          bond(newfront, adjfront);
          if (checksubfaces) {
            tspivot(oldfront, checksh); 
            if (checksh.sh != dummysh) {
              tsbond(newfront, checksh);
            }
          }
          if (flipque != (queue *) NULL) {
            // Only queue the faces of the two new tets.
            if (j < 2) enqueueflipface(newfront, flipque);
          }
        }
        for (j = 0; j < n; j++) {
          // Get an old tet: [0]a.b.p_0.p_n-1 or [j]a.b.p_j.p_j-1, (j > 0).
          oldfront = abtetlist[(i + j) % n];
          esymself(oldfront);
          enext2fnextself(oldfront);
          // Get an adjacent tet at face: [0]b.p_0.p_n-1 or [j]b.p_j.p_j-1.
          sym(oldfront, adjfront); // adjfront may be dummy.
          // Get the corresponding face from the new tets.
          if (j == 0) {
            enextfnext(newtetlist[1], newfront); // b.p_0.n_n-1
          } else if (j == 1) {
            enext2fnext(newtetlist[1], newfront); // b.p_1.p_0
          } else { // j >= 2.
            enextfnext(tmpabtetlist[j - 1], newfront); // b.p_j.p_j-1
          }
          bond(newfront, adjfront);
          if (checksubfaces) {
            tspivot(oldfront, checksh); 
            if (checksh.sh != dummysh) {
              tsbond(newfront, checksh);
            }
          }
          if (flipque != (queue *) NULL) {
            // Only queue the faces of the two new tets.
            if (j < 2) enqueueflipface(newfront, flipque);
          }
        }
        // Adjust the faces in the temporary new tets at ab for recursively
        //   processing on the n-1 tets.(See the description at beginning)
        for (j = 0; j < n - 1; j++) {
          fnextself(tmpabtetlist[j]);
        }
        if (n > 4) {
          success = removeedgebytranNM(&tmpkey, n-1, tmpabtetlist,
            &(newtetlist[2]), NULL, NULL, flipque);
        } else { // //assert(n == 4);
          success = removeedgebyflip32(&tmpkey, tmpabtetlist,
            &(newtetlist[2]), flipque);
        }
        // No matter it was success or not, delete the temporary tets.
        for (j = 0; j < n - 1; j++) {
          tetrahedrondealloc(tmpabtetlist[j].tet);
        }
        if (success) {
          // The new configuration is good. 
          // Do not delete the old tets.
          // for (j = 0; j < n; j++) {
          //   tetrahedrondealloc(abtetlist[j].tet);
          // }
          // Save the minimal improved quality value.
          if (key != (REAL *) NULL) {
            *key = (tmpkey < cosmaxd ? tmpkey : cosmaxd);
          }
          return true;
        } else {
          // The new configuration is bad, substitue back the old tets.
          if (elemfliplist != NULL) {
            // Remove the last registered 2-to-3 flip.
            elemfliplist->objects--;
          }
          for (j = 0; j < n; j++) {
            oldfront = abtetlist[(i + j) % n];
            esymself(oldfront);
            enextfnextself(oldfront); // [0]a.p_0.p_n-1, [j]a.p_j.p_j-1.
            sym(oldfront, adjfront); // adjfront may be dummy.
            bond(oldfront, adjfront);
            if (checksubfaces) {
              tspivot(oldfront, checksh);
              if (checksh.sh != dummysh) {
                tsbond(oldfront, checksh);
              }
            }
          }
          for (j = 0; j < n; j++) {
            oldfront = abtetlist[(i + j) % n];
            esymself(oldfront);
            enext2fnextself(oldfront); // [0]b.p_0.p_n-1, [j]b.p_j.p_j-1.
            sym(oldfront, adjfront); // adjfront may be dummy
            bond(oldfront, adjfront);
            if (checksubfaces) {
              tspivot(oldfront, checksh);
              if (checksh.sh != dummysh) {
                tsbond(oldfront, checksh);
              }
            }
          }
          // Delete the new tets.
          tetrahedrondealloc(newtetlist[0].tet);
          tetrahedrondealloc(newtetlist[1].tet);
          // If tmpkey has been modified, then the failure was not due to
          //   unflipable configuration, but the non-improvement.
          if (key && (tmpkey < *key)) {
            *key = tmpkey;
            return false;
          }
        } // if (success)
      } // if (doflip)
    } // if (ori > 0.0)
  } // for (i = 0; i < n; i++)

  return false;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// removeedgebycombNM()    Remove an edge by combining two flipNMs.          //
//                                                                           //
// Given a set T of tets surrounding edge ab. The premise is that ab can not //
// be removed by a flipNM. This routine attempts to remove ab by two flipNMs,//
// i.e., first find and flip an edge af (or bf) by flipNM, then flip ab by   //
// flipNM. If it succeeds, two sets T(ab) and T(af) of tets are replaced by  //
// a new set T' and both ab and af are not edges in T' anymore.              //
//                                                                           //
// 'abtetlist' contains n tets sharing ab. Imaging that ab is perpendicular  //
// to the screen, such that a lies in front of and b lies behind it. Let the //
// projections of the n apexes on the screen in clockwise order are: p_0,...,//
// p_n-1, respectively. So the list of tets are: [0]abp_0p_n-1, [1]abp_1p_0, //
// ..., [n-1]abp_n-1p_n-2, respectively.                                     //
//                                                                           //
// The principle of the approach is: for a face a.b.p_0, check if edge b.p_0 //
// is of type N32 (or N44). If it is, then try to do a flipNM on it. If the  //
// flip is successful, then try to do another flipNM on a.b.  If one of the  //
// two flipNMs fails, restore the old tets as they have never been flipped.  //
// Then try the next face a.b.p_1.  The process can be looped for all faces  //
// having ab. Stop if ab is removed or all faces have been visited. Note in  //
// the above description only b.p_0 is considered, a.p_0 is done by swapping //
// the position of a and b.                                                  //
//                                                                           //
// Similar operations have been described in [Joe,1995].  My approach checks //
// more cases for finding flips than Joe's.  For instance, the cases (1)-(7) //
// of Joe only consider abf for finding a flip (T23/T32).  My approach looks //
// all faces at ab for finding flips. Moreover, the flipNM can flip an edge  //
// whose star may have more than 3 tets while Joe's only works on 3-tet case.//
//                                                                           //
// If ab is removed, 'newtetlist' contains the new tets. Two sets 'abtetlist'//
// (n tets) and 'bftetlist' (n1 tets) have been replaced.  The number of new //
// tets can be calculated by follows: the 1st flip transforms n1 tets into   //
// (n1 - 2) * 2 new tets, however,one of the new tets goes into the new link //
// of ab, i.e., the reduced tet number in Star(ab) is n - 1;  the 2nd flip   //
// transforms n - 1 tets into (n - 3) * 2 new tets. Hence the number of new  //
// tets are: m = ((n1 - 2) * 2 - 1) + (n - 3) * 2.  The old tets are NOT del-//
// eted. The caller has the right to delete them or reverse the operation.   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::removeedgebycombNM(REAL *key, int n, triface *abtetlist,
  int *n1, triface *bftetlist, triface *newtetlist, queue *flipque)
{
  triface tmpabtetlist[21];
  triface newfront, oldfront, adjfront;
  face checksh;
  point pa, pb, p[21];
  REAL ori, tmpkey, tmpkey2;
  REAL attrib, volume;
  bool doflip, success;
  int twice, count;
  int i, j, k, m;

  // point *ppt; // Used together with fixededgelist.
  long bakflipcount; // Used for elemfliplist.

  // Maximal 20 tets in Star(ab).
  //assert(n < 20); // n <= b->maxflipedgelinksize

  // Do the following procedure twice, one for flipping edge b.p_0 and the
  //   other for p_0.a which is symmetric to the first.
  twice = 0;
  do {
    // Two points a and b are fixed.
    pa = org(abtetlist[0]);
    pb = dest(abtetlist[0]);
    // The points p_0, ..., p_n-1 are permuted in the following loop.
    for (i = 0; i < n; i++) {
      // Get the n points for the current configuration.
      for (j = 0; j < n; j++) {
        p[j] = apex(abtetlist[(i + j) % n]);
      }
      // Check if b.p_0 is of type N32 or N44.
      ori = orient3d(pb, p[0], p[1], p[n - 1]);
      if ((ori > 0) && (key != (REAL *) NULL)) {
        // b.p_0 is not N32. However, it is possible that the tet b.p_0.p_1.
        //   p_n-1 has worse quality value than the key. In such case, also
        //   try to flip b.p_0.
        tetalldihedral(pb, p[0], p[n - 1], p[1], NULL, &tmpkey, NULL);
        if (tmpkey < *key) ori = 0.0;
      }
      if ((fixededgelist != NULL) && (ori <= 0.0)) {
        // b.p_0 is either N32 or N44. Do not flip a fixed edge.
        if (check4fixededge(pb, p[0])) {
          ori = 1.0; // Do not flip this edge. Skip it.
        }
      }
      if (ori <= 0.0) {
        // b.p_0 is either N32 or N44. Try the 1st flipNM.
        bftetlist[0] = abtetlist[i];
        enextself(bftetlist[0]);// go to edge b.p_0.
        adjustedgering(bftetlist[0], CW); // edge p_0.b.
        //assert(apex(bftetlist[0]) == pa);
        // Form Star(b.p_0).
        doflip = true;
        *n1 = 0;
        do {
          // Is the list full?
          if (*n1 == 20) break;
          if (checksubfaces) {
            // Stop if a subface appears.
            tspivot(bftetlist[*n1], checksh);
            if (checksh.sh != dummysh) {
              doflip = false; break;
            }
          }
          // Get the next tet at p_0.b.
          if (!fnext(bftetlist[*n1], bftetlist[(*n1) + 1])) {
            // Meet a boundary face. Do not flip.
            doflip = false; break;
          }
          (*n1)++;
        } while (apex(bftetlist[*n1]) != pa);
        // 2 < n1 <= b->maxflipedgelinksize.
        if (doflip) {
          success = false;
          tmpkey = -1.0;  // = acos(pi).
          if (key != (REAL *) NULL) tmpkey = *key;
          m = 0;
          if (*n1 == 3) {
            // Three tets case. Try flip32.
            success = removeedgebyflip32(&tmpkey,bftetlist,newtetlist,flipque);
            m = 2;
          } else if ((*n1 > 3) && (*n1 <= b->maxflipedgelinksize)) {
            // Four or more tets case. Try flipNM.
            success = removeedgebytranNM(&tmpkey, *n1, bftetlist, newtetlist,
                                         p[1], p[n - 1], flipque);
            // If success, the number of new tets.
            m = ((*n1) - 2) * 2;
          } else {
            if (b->verbose > 1) {
              //printf("  !! Unhandled case: n1 = %d.\n", *n1);
            }
          }
          if (success) {
            // b.p_0 is flipped. The link of ab is reduced (by 1), i.e., p_0
            //   is not on the link of ab. Two old tets a.b.p_0.p_n-1 and
            //   a.b.p_1.p_0 have been removed from the Star(ab) and one new
            //   tet t = a.b.p_1.p_n-1 belongs to Star(ab). 
            // Find t in the 'newtetlist' and remove it from the list.
            setpointmark(pa, -pointmark(pa) - 1);
            setpointmark(pb, -pointmark(pb) - 1);
            //assert(m > 0);
            for (j = 0; j < m; j++) {
              tmpabtetlist[0] = newtetlist[j];
              // Does it has ab?
              count = 0;
              for (k = 0; k < 4; k++) {
                if (pointmark((point)(tmpabtetlist[0].tet[4+k])) < 0) count++;
              }
              if (count == 2) {
                // It is. Adjust t to be the edge ab.
                for (tmpabtetlist[0].loc = 0; tmpabtetlist[0].loc < 4;
                     tmpabtetlist[0].loc++) {
                  if ((oppo(tmpabtetlist[0]) != pa) &&
                      (oppo(tmpabtetlist[0]) != pb)) break;
                }
                // The face of t must contain ab.
                //assert(tmpabtetlist[0].loc < 4);
                findedge(&(tmpabtetlist[0]), pa, pb);
                break;
              }
            }
            //assert(j < m); // The tet must exist.
            // Remove t from list. Fill t's position by the last tet.
            newtetlist[j] = newtetlist[m - 1];
            setpointmark(pa, -(pointmark(pa) + 1));
            setpointmark(pb, -(pointmark(pb) + 1));
            // Create the temporary Star(ab) for the next flipNM.
            adjustedgering(tmpabtetlist[0], CCW);
            if (org(tmpabtetlist[0]) != pa) {
              fnextself(tmpabtetlist[0]);
              esymself(tmpabtetlist[0]);
            }
#ifdef SELF_CHECK
            // Make sure current edge is a->b.
            //assert(org(tmpabtetlist[0]) == pa);
            //assert(dest(tmpabtetlist[0]) == pb);
            //assert(apex(tmpabtetlist[0]) == p[n - 1]);
            //assert(oppo(tmpabtetlist[0]) == p[1]);
#endif // SELF_CHECK
            // There are n - 2 left temporary tets.
            for (j = 1; j < n - 1; j++) {
              maketetrahedron(&(tmpabtetlist[j]));
              setorg(tmpabtetlist[j], pa);
              setdest(tmpabtetlist[j], pb);
              setapex(tmpabtetlist[j], p[j]);
              setoppo(tmpabtetlist[j], p[j + 1]);
            }
            // Transfer the element attributes.
            for (j = 0; j < in->numberoftetrahedronattributes; j++) {
              attrib = elemattribute(abtetlist[0].tet, j);
              for (k = 0; k < n - 1; k++) {
                setelemattribute(tmpabtetlist[k].tet, j, attrib);
              }
            }
            // Transfer the volume constraints.
            if (b->varvolume && !b->refine) {
              volume = volumebound(abtetlist[0].tet);
              for (k = 0; k < n - 1; k++) {
                setvolumebound(tmpabtetlist[k].tet, volume);
              }
            }
            // Glue n - 1 internal faces of Star(ab).
            for (j = 0; j < n - 1; j++) {
              fnext(tmpabtetlist[j], newfront);
              bond(newfront, tmpabtetlist[(j + 1) % (n - 1)]); // a.b.p_j+1
            }
            // Substitute the old tets with the new tets by connecting the
            //   new tets to the adjacent tets in the mesh. There are (n-2)
            //   * 2 (outer) faces of the new tets need to be operated.
            // Note that the old tets still have the pointers to their
            //   adjacent tets in the mesh.  These pointers can be re-used
            //   to inverse the substitution.
            for (j = 2; j < n; j++) {
              // Get an old tet: [j]a.b.p_j.p_j-1, (j > 1).
              oldfront = abtetlist[(i + j) % n];
              esymself(oldfront);
              enextfnextself(oldfront);
              // Get an adjacent tet at face: [j]a.p_j.p_j-1.
              sym(oldfront, adjfront); // adjfront may be dummy.
              // Get the corresponding face from the new tets.
              // j >= 2.
              enext2fnext(tmpabtetlist[j - 1], newfront); // a.p_j.p_j-1
              bond(newfront, adjfront);
              if (checksubfaces) {
                tspivot(oldfront, checksh); 
                if (checksh.sh != dummysh) {
                  tsbond(newfront, checksh);
                }
              }
            }
            for (j = 2; j < n; j++) {
              // Get an old tet: [j]a.b.p_j.p_j-1, (j > 2).
              oldfront = abtetlist[(i + j) % n];
              esymself(oldfront);
              enext2fnextself(oldfront);
              // Get an adjacent tet at face: [j]b.p_j.p_j-1.
              sym(oldfront, adjfront); // adjfront may be dummy.
              // Get the corresponding face from the new tets.
              // j >= 2.
              enextfnext(tmpabtetlist[j - 1], newfront); // b.p_j.p_j-1
              bond(newfront, adjfront);
              if (checksubfaces) {
                tspivot(oldfront, checksh); 
                if (checksh.sh != dummysh) {
                  tsbond(newfront, checksh);
                }
              }
            }
            // Adjust the faces in the temporary new tets at ab for
            //   recursively processing on the n-1 tets.
            for (j = 0; j < n - 1; j++) {
              fnextself(tmpabtetlist[j]);
            }
            tmpkey2 = -1;
            if (key) tmpkey2 = *key;
            if (elemfliplist != NULL) {
              // Remember the current registered flips.
              bakflipcount = elemfliplist->objects;
            }
            if ((n - 1) == 3) {
              success = removeedgebyflip32(&tmpkey2, tmpabtetlist,
                &(newtetlist[m - 1]), flipque);
            } else { // //assert((n - 1) >= 4);
              success = removeedgebytranNM(&tmpkey2, n - 1, tmpabtetlist,
                &(newtetlist[m - 1]), NULL, NULL, flipque);
            }
            // No matter it was success or not, delete the temporary tets.
            for (j = 0; j < n - 1; j++) {
              tetrahedrondealloc(tmpabtetlist[j].tet);
            }
            if (success) {
              // The new configuration is good. 
              // Do not delete the old tets.
              // for (j = 0; j < n; j++) {
              //   tetrahedrondealloc(abtetlist[j].tet);
              // }
              // Return the bigger dihedral in the two sets of new tets.
              if (key != (REAL *) NULL) {
                *key = tmpkey2 < tmpkey ? tmpkey2 : tmpkey;
              }
              return true;
            } else {
              // The new configuration is bad, substitue back the old tets.
              if (elemfliplist != NULL) {
                // Restore the registered flips.
                elemfliplist->objects = bakflipcount;
              }
              for (j = 0; j < n; j++) {
                oldfront = abtetlist[(i + j) % n];
                esymself(oldfront);
                enextfnextself(oldfront); // [0]a.p_0.p_n-1, [j]a.p_j.p_j-1.
                sym(oldfront, adjfront); // adjfront may be dummy.
                bond(oldfront, adjfront);
                if (checksubfaces) {
                  tspivot(oldfront, checksh);
                  if (checksh.sh != dummysh) {
                    tsbond(oldfront, checksh);
                  }
                }
              }
              for (j = 0; j < n; j++) {
                oldfront = abtetlist[(i + j) % n];
                esymself(oldfront);
                enext2fnextself(oldfront); // [0]b.p_0.p_n-1, [j]b.p_j.p_j-1.
                sym(oldfront, adjfront); // adjfront may be dummy
                bond(oldfront, adjfront);
                if (checksubfaces) {
                  tspivot(oldfront, checksh);
                  if (checksh.sh != dummysh) {
                    tsbond(oldfront, checksh);
                  }
                }
              }
              // Substitute back the old tets of the first flip.
              for (j = 0; j < *n1; j++) {
                oldfront = bftetlist[j];
                esymself(oldfront);
                enextfnextself(oldfront);
                sym(oldfront, adjfront); // adjfront may be dummy.
                bond(oldfront, adjfront);
                if (checksubfaces) {
                  tspivot(oldfront, checksh);
                  if (checksh.sh != dummysh) {
                    tsbond(oldfront, checksh);
                  }
                }
              }
              for (j = 0; j < *n1; j++) {
                oldfront = bftetlist[j];
                esymself(oldfront);
                enext2fnextself(oldfront); // [0]b.p_0.p_n-1, [j]b.p_j.p_j-1.
                sym(oldfront, adjfront); // adjfront may be dummy
                bond(oldfront, adjfront);
                if (checksubfaces) {
                  tspivot(oldfront, checksh);
                  if (checksh.sh != dummysh) {
                    tsbond(oldfront, checksh);
                  }
                }
              }
              // Delete the new tets of the first flip. Note that one new
              //   tet has already been removed from the list.
              for (j = 0; j < m - 1; j++) {
                tetrahedrondealloc(newtetlist[j].tet);
              }
            } // if (success)
          } // if (success)
        } // if (doflip)
      } // if (ori <= 0.0)
    } // for (i = 0; i < n; i++)
    // Inverse a and b and the tets configuration.
    for (i = 0; i < n; i++) newtetlist[i] = abtetlist[i];
    for (i = 0; i < n; i++) {
      oldfront = newtetlist[n - i - 1];
      esymself(oldfront);
      fnextself(oldfront);
      abtetlist[i] = oldfront;
    }
    twice++;
  } while (twice < 2);

  return false;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// splittetrahedron()    Insert a point into a tetrahedron, split it into    //
//                       four tetrahedra.                                    //
//                                                                           //
// The tetrahedron is given by 'splittet'.  Let it is abcd.  The inserting   //
// point 'newpoint' v should lie strictly inside abcd.                       //
//                                                                           //
// Splitting a tetrahedron is to shrink abcd to abcv,  and create three new  //
// tetrahedra badv, cbdv, and acdv.                                          //
//                                                                           //
// On completion, 'splittet' returns abcv.  If 'flipqueue' is not NULL, it   //
// contains all possibly non-locally Delaunay faces.                         //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::splittetrahedron(point newpoint, triface* splittet,
  queue* flipqueue)
{
  triface oldabd, oldbcd, oldcad;                      // Old configuration.
  triface abdcasing, bcdcasing, cadcasing;
  face abdsh, bcdsh, cadsh;
  triface abcv, badv, cbdv, acdv;                      // New configuration.
  triface worktet;
  face abseg, bcseg, caseg;
  face adseg, bdseg, cdseg;
  point pa, pb, pc, pd;
  REAL attrib, volume;
  int i;

  abcv = *splittet;
  abcv.ver = 0;
  // Set the changed vertices and new tetrahedron.
  pa = org(abcv);
  pb = dest(abcv);
  pc = apex(abcv);
  pd = oppo(abcv);

  if (b->verbose > 1) {
    //printf("  Inserting point %d in tetrahedron (%d, %d, %d, %d).\n",
          // pointmark(newpoint), pointmark(pa), pointmark(pb), pointmark(pc),
          // pointmark(pd));
  }

  fnext(abcv, oldabd);
  enextfnext(abcv, oldbcd);
  enext2fnext(abcv, oldcad);
  sym(oldabd, abdcasing);
  sym(oldbcd, bcdcasing);
  sym(oldcad, cadcasing);
  maketetrahedron(&badv);
  maketetrahedron(&cbdv);
  maketetrahedron(&acdv);

  // Set 'badv' vertices.
  setorg (badv, pb);
  setdest(badv, pa);
  setapex(badv, pd);
  setoppo(badv, newpoint);
  // Set 'cbdv' vertices.
  setorg (cbdv, pc);
  setdest(cbdv, pb);
  setapex(cbdv, pd);
  setoppo(cbdv, newpoint);
  // Set 'acdv' vertices.
  setorg (acdv, pa);
  setdest(acdv, pc);
  setapex(acdv, pd);
  setoppo(acdv, newpoint);
  // Set 'abcv' vertices
  setoppo(abcv, newpoint);

  // Set the element attributes of the new tetrahedra.
  for (i = 0; i < in->numberoftetrahedronattributes; i++) {
    attrib = elemattribute(abcv.tet, i);
    setelemattribute(badv.tet, i, attrib);
    setelemattribute(cbdv.tet, i, attrib);
    setelemattribute(acdv.tet, i, attrib);
  }
  // Set the volume constraint of the new tetrahedra.
  if (b->varvolume) {
    volume = volumebound(abcv.tet);
    setvolumebound(badv.tet, volume);
    setvolumebound(cbdv.tet, volume);
    setvolumebound(acdv.tet, volume);
  }

  // Bond the new triangles to the surrounding tetrahedron.
  bond(badv, abdcasing);
  bond(cbdv, bcdcasing);
  bond(acdv, cadcasing);
  // There may exist subfaces need to be bonded to the new tetrahedra.
  if (checksubfaces) {
    tspivot(oldabd, abdsh);
    if (abdsh.sh != dummysh) {
      tsdissolve(oldabd);
      tsbond(badv, abdsh);
    }
    tspivot(oldbcd, bcdsh);
    if (bcdsh.sh != dummysh) {
      tsdissolve(oldbcd);
      tsbond(cbdv, bcdsh);
    }
    tspivot(oldcad, cadsh);
    if (cadsh.sh != dummysh) {
      tsdissolve(oldcad);
      tsbond(acdv, cadsh);
    }
  } else if (checksubsegs) {
    tsspivot1(abcv, abseg);
    if (abseg.sh != dummysh) {
      tssbond1(badv, abseg);
    }
    enext(abcv, worktet);
    tsspivot1(worktet, bcseg);
    if (bcseg.sh != dummysh) {
      tssbond1(cbdv, bcseg);
    }
    enext2(abcv, worktet);
    tsspivot1(worktet, caseg);
    if (caseg.sh != dummysh) {
      tssbond1(acdv, caseg);
    }
    fnext(abcv, worktet);
    enext2self(worktet);
    tsspivot1(worktet, adseg);
    if (adseg.sh != dummysh) {
      tssdissolve1(worktet);
      enext(badv, worktet);
      tssbond1(worktet, adseg);
      enext2(acdv, worktet);
      tssbond1(worktet, adseg);
    }
    enextfnext(abcv, worktet);
    enext2self(worktet);
    tsspivot1(worktet, bdseg);
    if (bdseg.sh != dummysh) {
      tssdissolve1(worktet);
      enext(cbdv, worktet);
      tssbond1(worktet, bdseg);
      enext2(badv, worktet);
      tssbond1(worktet, bdseg);
    }
    enext2fnext(abcv, worktet);
    enext2self(worktet);
    tsspivot1(worktet, cdseg);
    if (cdseg.sh != dummysh) {
      tssdissolve1(worktet);
      enext(acdv, worktet);
      tssbond1(worktet, cdseg);
      enext2(cbdv, worktet);
      tssbond1(worktet, cdseg);
    }
  }
  badv.loc = 3; 
  cbdv.loc = 2;
  bond(badv, cbdv);
  cbdv.loc = 3; 
  acdv.loc = 2;
  bond(cbdv, acdv);
  acdv.loc = 3; 
  badv.loc = 2;
  bond(acdv, badv);
  badv.loc = 1; 
  bond(badv, oldabd);
  cbdv.loc = 1; 
  bond(cbdv, oldbcd);
  acdv.loc = 1; 
  bond(acdv, oldcad);
  
  badv.loc = 0;
  cbdv.loc = 0;
  acdv.loc = 0;
  if (b->verbose > 3) {
    //printf("    Updating abcv ");
    printtet(&abcv);
    //printf("    Creating badv ");
    printtet(&badv);
    //printf("    Creating cbdv ");
    printtet(&cbdv);
    //printf("    Creating acdv ");
    printtet(&acdv);
  }

  if (flipqueue != (queue *) NULL) {
    enqueueflipface(abcv, flipqueue);
    enqueueflipface(badv, flipqueue);
    enqueueflipface(cbdv, flipqueue);
    enqueueflipface(acdv, flipqueue);
  }

  // Save a handle for quick point location.
  recenttet = abcv;
  // Set the return handle be abcv.
  *splittet = abcv;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// splittetface()    Insert a point on a face of a mesh.                     //
//                                                                           //
// 'splittet' is the splitting face.  Let it is abcd, where abc is the face  //
// will be split. If abc is not a hull face, abce is the tetrahedron at the  //
// opposite of d.                                                            //
//                                                                           //
// To split face abc by a point v is to shrink the tetrahedra abcd to abvd,  //
// create two new tetrahedra bcvd, cavd.  If abc is not a hull face, shrink  //
// the tetrahedra bace to bave, create two new tetrahedra cbve, acve.        //
//                                                                           //
// If abc is a subface, it is split into three subfaces simultaneously by    //
// calling routine splitsubface(), hence, abv, bcv, cav.  The edge rings of  //
// the split subfaces have the same orientation as abc's.                    //
//                                                                           //
// On completion, 'splittet' returns abvd.  If 'flipqueue' is not NULL, it   //
// contains all possibly non-locally Delaunay faces.                         //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::splittetface(point newpoint, triface* splittet,
  queue* flipqueue)
{
  triface abcd, bace;                                  // Old configuration.
  triface oldbcd, oldcad, oldace, oldcbe; 
  triface bcdcasing, cadcasing, acecasing, cbecasing;
  face abcsh, bcdsh, cadsh, acesh, cbesh;
  triface abvd, bcvd, cavd, bave, cbve, acve;          // New configuration.
  triface worktet;
  face bcseg, caseg;
  face adseg, bdseg, cdseg;
  face aeseg, beseg, ceseg;
  point pa, pb, pc, pd, pe;
  REAL attrib, volume;
  bool mirrorflag;
  int i;

  abcd = *splittet;
  // abcd.ver = 0; // Adjust to be CCW edge ring.
  adjustedgering(abcd, CCW);
  pa = org(abcd);
  pb = dest(abcd);
  pc = apex(abcd);
  pd = oppo(abcd);
  pe = (point) NULL; // avoid a compile warning.
  // Is there a second tetrahderon?
  mirrorflag = issymexist(&abcd);
  if (mirrorflag) {
    // This is an interior face.
    sym(abcd, bace);
    findedge(&bace, dest(abcd), org(abcd));
    pe = oppo(bace);
  }
  if (checksubfaces) {
    // Is there a subface need to be split together?
    tspivot(abcd, abcsh);
    if (abcsh.sh != dummysh) {
      // Exists! Keep the edge ab of both handles be the same.
      findedge(&abcsh, org(abcd), dest(abcd));
    }
  }

  if (b->verbose > 1) {
    //printf("  Inserting point %d on face (%d, %d, %d).\n", pointmark(newpoint),
           //pointmark(pa), pointmark(pb), pointmark(pc));
  }

  // Save the old configuration at faces bcd and cad.
  enextfnext(abcd, oldbcd);
  enext2fnext(abcd, oldcad);
  sym(oldbcd, bcdcasing);
  sym(oldcad, cadcasing);
  // Create two new tetrahedra.
  maketetrahedron(&bcvd);
  maketetrahedron(&cavd);
  if (mirrorflag) {
    // Save the old configuration at faces bce and cae.
    enextfnext(bace, oldace);
    enext2fnext(bace, oldcbe);
    sym(oldace, acecasing);
    sym(oldcbe, cbecasing);
    // Create two new tetrahedra.
    maketetrahedron(&acve);
    maketetrahedron(&cbve);
  } else {
    // Splitting a boundary face increases the number of boundary faces.
    hullsize += 2;
  }

  // Set vertices to the changed tetrahedron and new tetrahedra.
  abvd = abcd;  // Update 'abcd' to 'abvd'.
  setapex(abvd, newpoint);
  setorg (bcvd, pb);  // Set 'bcvd'.
  setdest(bcvd, pc);
  setapex(bcvd, newpoint);
  setoppo(bcvd, pd);
  setorg (cavd, pc);  // Set 'cavd'.
  setdest(cavd, pa);
  setapex(cavd, newpoint);
  setoppo(cavd, pd);
  // Set the element attributes of the new tetrahedra.
  for (i = 0; i < in->numberoftetrahedronattributes; i++) {
    attrib = elemattribute(abvd.tet, i);
    setelemattribute(bcvd.tet, i, attrib);
    setelemattribute(cavd.tet, i, attrib);
  }
  if (b->varvolume) {
    // Set the area constraint of the new tetrahedra.
    volume = volumebound(abvd.tet);
    setvolumebound(bcvd.tet, volume);
    setvolumebound(cavd.tet, volume);
  }
  if (mirrorflag) {
    bave = bace;  // Update 'bace' to 'bave'.
    setapex(bave, newpoint);
    setorg (acve, pa);  // Set 'acve'.
    setdest(acve, pc);
    setapex(acve, newpoint);
    setoppo(acve, pe);
    setorg (cbve, pc);  // Set 'cbve'.
    setdest(cbve, pb);
    setapex(cbve, newpoint);
    setoppo(cbve, pe);
    // Set the element attributes of the new tetrahedra.
    for (i = 0; i < in->numberoftetrahedronattributes; i++) {
      attrib = elemattribute(bave.tet, i);
      setelemattribute(acve.tet, i, attrib);
      setelemattribute(cbve.tet, i, attrib);
    }
    if (b->varvolume) {
      // Set the area constraint of the new tetrahedra.
      volume = volumebound(bave.tet);
      setvolumebound(acve.tet, volume);
      setvolumebound(cbve.tet, volume);
    }
  }

  // Bond the new tetrahedra to the surrounding tetrahedra.
  bcvd.loc = 1;
  bond(bcvd, bcdcasing); 
  cavd.loc = 1;
  bond(cavd, cadcasing); 
  bcvd.loc = 3;
  bond(bcvd, oldbcd);
  cavd.loc = 2;
  bond(cavd, oldcad);
  bcvd.loc = 2;
  cavd.loc = 3;
  bond(bcvd, cavd);  
  if (mirrorflag) {
    acve.loc = 1;
    bond(acve, acecasing);
    cbve.loc = 1;
    bond(cbve, cbecasing);
    acve.loc = 3;
    bond(acve, oldace);
    cbve.loc = 2;
    bond(cbve, oldcbe);
    acve.loc = 2;
    cbve.loc = 3;
    bond(acve, cbve);
    // Bond two new coplanar facets.
    bcvd.loc = 0;
    cbve.loc = 0;
    bond(bcvd, cbve);
    cavd.loc = 0;
    acve.loc = 0;
    bond(cavd, acve);
  }

  // There may exist subface needed to be bonded to the new tetrahedra.
  if (checksubfaces) {
    tspivot(oldbcd, bcdsh);
    if (bcdsh.sh != dummysh) {
      tsdissolve(oldbcd);
      bcvd.loc = 1;
      tsbond(bcvd, bcdsh);
    }
    tspivot(oldcad, cadsh);
    if (cadsh.sh != dummysh) {
      tsdissolve(oldcad);
      cavd.loc = 1;
      tsbond(cavd, cadsh);
    }
    if (mirrorflag) {
      tspivot(oldace, acesh);
      if (acesh.sh != dummysh) {
        tsdissolve(oldace);
        acve.loc = 1;
        tsbond(acve, acesh);
      }
      tspivot(oldcbe, cbesh);
      if (cbesh.sh != dummysh) {
        tsdissolve(oldcbe);
        cbve.loc = 1;
        tsbond(cbve, cbesh);
      }
    }
    // Is there a subface needs to be split together?
    if (abcsh.sh != dummysh) {
      // Split this subface 'abc' into three i.e, abv, bcv, cav.
      splitsubface(newpoint, &abcsh, (queue *) NULL);
    }  
  } else if (checksubsegs) {
    // abvd.loc = abvd.ver = 0;
    bcvd.loc = bcvd.ver = 0;
    cavd.loc = cavd.ver = 0;
    if (mirrorflag) {
      // bave.loc = bave.ver = 0;
      cbve.loc = cbve.ver = 0;
      acve.loc = acve.ver = 0;
    }
    enext(abvd, worktet);
    tsspivot1(worktet, bcseg);
    if (bcseg.sh != dummysh) {
      tssdissolve1(worktet);
      tssbond1(bcvd, bcseg);
      if (mirrorflag) {
        enext2(bave, worktet);
        tssdissolve1(worktet);
        tssbond1(cbve, bcseg);
      }
    }
    enext2(abvd, worktet);
    tsspivot1(worktet, caseg);
    if (caseg.sh != dummysh) {
      tssdissolve1(worktet);
      tssbond1(cavd, caseg);
      if (mirrorflag) {
        enext(bave, worktet);
        tssdissolve1(worktet);
        tssbond1(acve, caseg);
      }
    }
    fnext(abvd, worktet);
    enext2self(worktet);
    tsspivot1(worktet, adseg);
    if (adseg.sh != dummysh) {
      fnext(cavd, worktet);
      enextself(worktet);
      tssbond1(worktet, adseg);
    }
    fnext(abvd, worktet);
    enextself(worktet);
    tsspivot1(worktet, bdseg);
    if (bdseg.sh != dummysh) {
      fnext(bcvd, worktet);
      enext2self(worktet);
      tssbond1(worktet, bdseg);
    }
    enextfnext(abvd, worktet);
    enextself(worktet);
    tsspivot1(worktet, cdseg);
    if (cdseg.sh != dummysh) {
      tssdissolve1(worktet);
      fnext(bcvd, worktet);
      enextself(worktet);
      tssbond1(worktet, cdseg);
      fnext(cavd, worktet);
      enext2self(worktet);
      tssbond1(worktet, cdseg);
    }
    if (mirrorflag) {
      fnext(bave, worktet);
      enextself(worktet);
      tsspivot1(worktet, aeseg);
      if (aeseg.sh != dummysh) {
        fnext(acve, worktet);
        enext2self(worktet);
        tssbond1(worktet, aeseg);
      }
      fnext(bave, worktet);
      enext2self(worktet);
      tsspivot1(worktet, beseg);
      if (beseg.sh != dummysh) {
        fnext(cbve, worktet);
        enextself(worktet);
        tssbond1(worktet, beseg);
      }
      enextfnext(bave, worktet);
      enextself(worktet);
      tsspivot1(worktet, ceseg);
      if (ceseg.sh != dummysh) {
        tssdissolve1(worktet);
        fnext(cbve, worktet);
        enext2self(worktet);
        tssbond1(worktet, ceseg);
        fnext(acve, worktet);
        enextself(worktet);
        tssbond1(worktet, ceseg);
      }
    }
  }

  // Save a handle for quick point location.
  recenttet = abvd;
  // Set the return handle be abvd.
  *splittet = abvd;

  bcvd.loc = 0;
  cavd.loc = 0;
  if (mirrorflag) {
    cbve.loc = 0;
    acve.loc = 0;
  }
  if (b->verbose > 3) {
    //printf("    Updating abvd ");
    printtet(&abvd);
    //printf("    Creating bcvd ");
    printtet(&bcvd);
    //printf("    Creating cavd ");
    printtet(&cavd);
    if (mirrorflag) {
      //printf("    Updating bave ");
      printtet(&bave);
      //printf("    Creating cbve ");
      printtet(&cbve);
      //printf("    Creating acve ");
      printtet(&acve);
    }
  }

  if (flipqueue != (queue *) NULL) {
    fnextself(abvd);
    enqueueflipface(abvd, flipqueue);
    fnextself(bcvd);
    enqueueflipface(bcvd, flipqueue);
    fnextself(cavd);
    enqueueflipface(cavd, flipqueue);
    if (mirrorflag) {
      fnextself(bave);
      enqueueflipface(bave, flipqueue);
      fnextself(cbve);
      enqueueflipface(cbve, flipqueue);
      fnextself(acve);
      enqueueflipface(acve, flipqueue);
    }
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// splitsubface()    Insert a point on a subface, split it into three.       //
//                                                                           //
// The subface is 'splitface'.  Let it is abc. The inserting point 'newpoint'//
// v should lie inside abc.  If the neighbor tetrahedra of abc exist, i.e.,  //
// abcd and bace, they should have been split by routine splittetface()      //
// before calling this routine, so the connection between the new tetrahedra //
// and new subfaces can be correctly set.                                    //
//                                                                           //
// To split subface abc by point v is to shrink abc to abv, create two new   //
// subfaces bcv and cav.  Set the connection between updated and new created //
// subfaces. If there is a subsegment at edge bc or ca, connection of new    //
// subface (bcv or cav) to its casing subfaces is a face link, 'casingin' is //
// the predecessor and 'casingout' is the successor. It is important to keep //
// the orientations of the edge rings of the updated and created subfaces be //
// the same as abc's. So they have the same orientation as other subfaces of //
// this facet with respect to the lift point of this facet.                  //
//                                                                           //
// On completion, 'splitface' returns abv.  If 'flipqueue' is not NULL, it   //
// returns all possibly non-Delaunay edges.                                  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::splitsubface(point newpoint, face* splitface,
  queue* flipqueue)
{
  triface abvd, bcvd, cavd, bave, cbve, acve;
  face abc, oldbc, oldca, bc, ca, spinsh;
  face bccasin, bccasout, cacasin, cacasout;
  face abv, bcv, cav;
  point pa, pb, pc;
  
  abc = *splitface;
  // The newly created subfaces will have the same edge ring as abc.
  adjustedgering(abc, CCW);
  pa = sorg(abc);
  pb = sdest(abc);
  pc = sapex(abc);

  if (b->verbose > 1) {
    //printf("  Inserting point %d on subface (%d, %d, %d).\n",
           //pointmark(newpoint), pointmark(pa), pointmark(pb), pointmark(pc));
  }

  // Save the old configuration at edge bc and ca.  Subsegments may appear
  //   at both sides, save the face links and dissolve them.
  senext(abc, oldbc);
  senext2(abc, oldca);
  spivot(oldbc, bccasout);
  sspivot(oldbc, bc);
  if (bc.sh != dummysh) {
    if (bccasout.sh != dummysh) {
      // 'oldbc' is not self-bonded.
      spinsh = bccasout;
      do {
        bccasin = spinsh;
        spivotself(spinsh);
      } while (spinsh.sh != oldbc.sh);
    }
    ssdissolve(oldbc);
  } 
  spivot(oldca, cacasout);
  sspivot(oldca, ca);
  if (ca.sh != dummysh) {
    if (cacasout.sh != dummysh) {
      // 'oldca' is not self-bonded.
      spinsh = cacasout;
      do {
        cacasin = spinsh;
        spivotself(spinsh);
      } while (spinsh.sh != oldca.sh);
    }
    ssdissolve(oldca);
  }
  // Create two new subfaces.
  makeshellface(subfaces, &bcv);
  makeshellface(subfaces, &cav);

  // Set the vertices of changed and new subfaces.
  abv = abc;  // Update 'abc' to 'abv'.
  setsapex(abv, newpoint);
  setsorg(bcv, pb);  // Set 'bcv'.
  setsdest(bcv, pc);
  setsapex(bcv, newpoint);
  setsorg(cav, pc);  // Set 'cav'.
  setsdest(cav, pa);
  setsapex(cav, newpoint);
  if (b->quality && varconstraint) {
    // Copy yhr area bound into the new subfaces.
    setareabound(bcv, areabound(abv));
    setareabound(cav, areabound(abv));
  }
  // Copy the boundary mark into the new subfaces.
  setshellmark(bcv, shellmark(abv));
  setshellmark(cav, shellmark(abv));  
  // Copy the subface type into the new subfaces.
  setshelltype(bcv, shelltype(abv));
  setshelltype(cav, shelltype(abv));
  if (checkpbcs) {
    // Copy the pbcgroup into the new subfaces.
    setshellpbcgroup(bcv, shellpbcgroup(abv));
    setshellpbcgroup(cav, shellpbcgroup(abv));
  }
  // Bond the new subfaces to the surrounding subfaces.
  if (bc.sh != dummysh) {
    if (bccasout.sh != dummysh) {
      sbond1(bccasin, bcv);
      sbond1(bcv, bccasout);
    } else {
      // Bond 'bcv' to itsself.
      sdissolve(bcv); // sbond(bcv, bcv);
    }
    ssbond(bcv, bc);
  } else {
    sbond(bcv, bccasout);
  }
  if (ca.sh != dummysh) {
    if (cacasout.sh != dummysh) {
      sbond1(cacasin, cav);
      sbond1(cav, cacasout);
    } else {
      // Bond 'cav' to itself.
      sdissolve(cav); // sbond(cav, cav);
    }
    ssbond(cav, ca);
  } else {
    sbond(cav, cacasout);
  }
  senext2self(bcv);
  sbond(bcv, oldbc);
  senextself(cav);
  sbond(cav, oldca);
  senext2self(bcv);
  senextself(cav);
  sbond(bcv, cav);

  // Bond the new subfaces to the new tetrahedra if they exist.
  stpivot(abv, abvd);
  if (abvd.tet != dummytet) {
    // Get two new tetrahedra and their syms.
    findedge(&abvd, sorg(abv), sdest(abv));
    enextfnext(abvd, bcvd);
#ifdef SELF_CHECK
    //assert(bcvd.tet != dummytet);
#endif
    fnextself(bcvd);
    enext2fnext(abvd, cavd);
#ifdef SELF_CHECK
    //assert(cavd.tet != dummytet);
#endif
    fnextself(cavd);
    // Bond two new subfaces to the two new tetrahedra.
    tsbond(bcvd, bcv);
    tsbond(cavd, cav);
  }
  // Set the connection at the other sides if the tetrahedra exist.
  sesymself(abv);  // bav
  stpivot(abv, bave);
  if (bave.tet != dummytet) {
    sesymself(bcv);  // cbv
    sesymself(cav);  // acv
    // Get two new tetrahedra and their syms.
    findedge(&bave, sorg(abv), sdest(abv));
    enextfnext(bave, acve);
#ifdef SELF_CHECK
    //assert(acve.tet != dummytet);
#endif
    fnextself(acve);
    enext2fnext(bave, cbve);
#ifdef SELF_CHECK
    //assert(cbve.tet != dummytet);
#endif
    fnextself(cbve);
    // Bond two new subfaces to the two new tetrahedra.
    tsbond(acve, cav);
    tsbond(cbve, bcv);
  }

  bcv.shver = 0;
  cav.shver = 0;
  if (b->verbose > 3) {
    //printf("    Updating abv ");
    printsh(&abv);
    //printf("    Creating bcv ");
    printsh(&bcv);
    //printf("    Creating cav ");
    printsh(&cav);
  }

  if (flipqueue != (queue *) NULL) {
    enqueueflipedge(abv, flipqueue);
    enqueueflipedge(bcv, flipqueue);
    enqueueflipedge(cav, flipqueue);
  }

  // Set the return handle be abv.
  *splitface = abv;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// splittetedge()    Insert a point on an edge of the mesh.                  //
//                                                                           //
// The edge is given by 'splittet'. Assume its four corners are a, b, n1 and //
// n2, where ab is the edge will be split. Around ab may exist any number of //
// tetrahedra. For convenience, they're ordered in a sequence following the  //
// right-hand rule with your thumb points from a to b. Let the vertex set of //
// these tetrahedra be {a, b, n1, n2, ..., n(i)}. NOTE the tetrahedra around //
// ab may not connect to each other (can only happen when ab is a subsegment,//
// hence some faces abn(i) are subfaces).  If ab is a subsegment, abn1 must  //
// be a subface.                                                             //
//                                                                           //
// To split edge ab by a point v is to split all tetrahedra containing ab by //
// v.  More specifically, for each such tetrahedron, an1n2b, it is shrunk to //
// an1n2v, and a new tetrahedra bn2n1v is created. If ab is a subsegment, or //
// some faces of the splitting tetrahedra are subfaces, they must be split   //
// either by calling routine 'splitsubedge()'.                               //
//                                                                           //
// On completion, 'splittet' returns avn1n2.  If 'flipqueue' is not NULL, it //
// returns all faces which may become non-Delaunay after this operation.     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::splittetedge(point newpoint, triface* splittet,
  queue* flipqueue)
{
  triface *bots, *newtops;
  triface oldtop, topcasing;
  triface spintet, tmpbond0, tmpbond1;
  face abseg, splitsh, topsh, spinsh;
  triface worktet;
  face n1n2seg, n2vseg, n1vseg;
  point pa, pb, n1, n2;
  REAL attrib, volume;
  int wrapcount, hitbdry;
  int i, j;

  if (checksubfaces) {
    // Is there a subsegment need to be split together?
    tsspivot(splittet, &abseg);
    if (abseg.sh != dummysh) {
      abseg.shver = 0;
      // Orient the edge direction of 'splittet' be abseg.
      if (org(*splittet) != sorg(abseg)) {
        esymself(*splittet);
      }
    }
  } 
  spintet = *splittet;
  pa = org(spintet);
  pb = dest(spintet);

  if (b->verbose > 1) {
    //printf("  Inserting point %d on edge (%d, %d).\n", 
           //pointmark(newpoint), pointmark(pa), pointmark(pb));
  }

  // Collect the tetrahedra containing the splitting edge (ab).
  n1 = apex(spintet);
  hitbdry = 0;
  wrapcount = 1;
  if (checksubfaces && abseg.sh != dummysh) {
    // It may happen that some tetrahedra containing ab (a subsegment) are
    //   completely disconnected with others. If it happens, use the face
    //   link of ab to cross the boundary. 
    while (true) {
      if (!fnextself(spintet)) {
        // Meet a boundary, walk through it.
        hitbdry ++;
        tspivot(spintet, spinsh);
#ifdef SELF_CHECK
        //assert(spinsh.sh != dummysh);
#endif
        findedge(&spinsh, pa, pb);
        sfnextself(spinsh);
        stpivot(spinsh, spintet);
#ifdef SELF_CHECK
        //assert(spintet.tet != dummytet);
#endif
        findedge(&spintet, pa, pb);
        // Remember this position (hull face) in 'splittet'.
        *splittet = spintet;
        // Split two hull faces increase the hull size;
        hullsize += 2;
      }
      if (apex(spintet) == n1) break;
      wrapcount ++;
    }
    if (hitbdry > 0) {
      wrapcount -= hitbdry;
    }
  } else {
    // All the tetrahedra containing ab are connected together. If there
    //   are subfaces, 'splitsh' keeps one of them.
    splitsh.sh = dummysh;
    while (hitbdry < 2) {
      if (checksubfaces && splitsh.sh == dummysh) {
        tspivot(spintet, splitsh);
      }
      if (fnextself(spintet)) {
        if (apex(spintet) == n1) break;
        wrapcount++;
      } else {
        hitbdry ++;
        if (hitbdry < 2) {
          esym(*splittet, spintet);
        }
      }
    }
    if (hitbdry > 0) {
      // ab is on the hull.
      wrapcount -= 1;
      // 'spintet' now is a hull face, inverse its edge direction.
      esym(spintet, *splittet);
      // Split two hull faces increases the number of hull faces.
      hullsize += 2;
    }
  }
  
  // Make arrays of updating (bot, oldtop) and new (newtop) tetrahedra.
  bots = new triface[wrapcount];
  newtops = new triface[wrapcount];
  // Spin around ab, gather tetrahedra and set up new tetrahedra. 
  spintet = *splittet;
  for (i = 0; i < wrapcount; i++) {
    // Get 'bots[i] = an1n2b'.
    enext2fnext(spintet, bots[i]);
    esymself(bots[i]);
    // Create 'newtops[i]'.
    maketetrahedron(&(newtops[i]));
    // Go to the next.
    fnextself(spintet);
    if (checksubfaces && abseg.sh != dummysh) {
      if (!issymexist(&spintet)) {
        // We meet a hull face, walk through it.
        tspivot(spintet, spinsh);
#ifdef SELF_CHECK
        //assert(spinsh.sh != dummysh);
#endif
        findedge(&spinsh, pa, pb);
        sfnextself(spinsh);
        stpivot(spinsh, spintet);
#ifdef SELF_CHECK
        //assert(spintet.tet != dummytet);
#endif
        findedge(&spintet, pa, pb);
      }
    }
  }
  
  // Set the vertices of updated and new tetrahedra.
  for (i = 0; i < wrapcount; i++) {
    // Update 'bots[i] = an1n2v'.
    setoppo(bots[i], newpoint);
    // Set 'newtops[i] = bn2n1v'.
    n1 = dest(bots[i]);
    n2 = apex(bots[i]);
    // Set 'newtops[i]'.
    setorg(newtops[i], pb);
    setdest(newtops[i], n2);
    setapex(newtops[i], n1);
    setoppo(newtops[i], newpoint);
    // Set the element attributes of a new tetrahedron.
    for (j = 0; j < in->numberoftetrahedronattributes; j++) {
      attrib = elemattribute(bots[i].tet, j);
      setelemattribute(newtops[i].tet, j, attrib);
    }
    if (b->varvolume) {
      // Set the area constraint of a new tetrahedron.
      volume = volumebound(bots[i].tet);
      setvolumebound(newtops[i].tet, volume);
    }
//#ifdef SELF_CHECK
    // Make sure no inversed tetrahedron has been created.
    volume = orient3d(pa, n1, n2, newpoint);
    if (volume >= 0.0) {
      ////printf("Internal error in splittetedge(): volume = %.12g.\n", volume);
      break;
    }
    volume = orient3d(pb, n2, n1, newpoint);
    if (volume >= 0.0) {
      ////printf("Internal error in splittetedge(): volume = %.12g.\n", volume);
      break;
    }
//#endif
  }

  if (i < wrapcount) {
    // Do not insert this point. It will result inverted or degenerated tet.
    // Restore have updated tets in "bots".
    for (; i >= 0; i--) {
      setoppo(bots[i], pb);
    }
    // Deallocate tets in "newtops".
    for (i = 0; i < wrapcount; i++) {
      tetrahedrondealloc(newtops[i].tet);
    }
    delete [] newtops;
    delete [] bots;
    return false;
  }

  // Bond newtops to topcasings and bots.
  for (i = 0; i < wrapcount; i++) {
    // Get 'oldtop = n1n2va' from 'bots[i]'.
    enextfnext(bots[i], oldtop);
    sym(oldtop, topcasing);
    bond(newtops[i], topcasing);
    if (checksubfaces) {
      tspivot(oldtop, topsh);
      if (topsh.sh != dummysh) {
        tsdissolve(oldtop);
        tsbond(newtops[i], topsh);
      }
    }
    enextfnext(newtops[i], tmpbond0);
    bond(oldtop, tmpbond0);
  }
  // Bond between newtops.
  fnext(newtops[0], tmpbond0);
  enext2fnext(bots[0], spintet); 
  for (i = 1; i < wrapcount; i ++) {
    if (issymexist(&spintet)) {
      enext2fnext(newtops[i], tmpbond1);
      bond(tmpbond0, tmpbond1);
    }
    fnext(newtops[i], tmpbond0);
    enext2fnext(bots[i], spintet); 
  }
  // Bond the last to the first if no boundary.
  if (issymexist(&spintet)) {
    enext2fnext(newtops[0], tmpbond1);
    bond(tmpbond0, tmpbond1);
  }
  if (checksubsegs) {
    for (i = 0; i < wrapcount; i++) {
      enextfnext(bots[i], worktet); // edge n1->n2.
      tsspivot1(worktet, n1n2seg);
      if (n1n2seg.sh != dummysh) {
        enext(newtops[i], tmpbond0);
        tssbond1(tmpbond0, n1n2seg);
      }
      enextself(worktet); // edge n2->v ==> n2->b
      tsspivot1(worktet, n2vseg);
      if (n2vseg.sh != dummysh) {
        tssdissolve1(worktet);
        tssbond1(newtops[i], n2vseg);
      }
      enextself(worktet); // edge v->n1 ==> b->n1
      tsspivot1(worktet, n1vseg);
      if (n1vseg.sh != dummysh) {
        tssdissolve1(worktet);
        enext2(newtops[i], tmpbond0);
        tssbond1(tmpbond0, n1vseg);
      }
    }
  }

  // Is there exist subfaces and subsegment need to be split?
  if (checksubfaces) {
    if (abseg.sh != dummysh) {
      // A subsegment needs be split.
      spivot(abseg, splitsh);
#ifdef SELF_CHECK
      //assert(splitsh.sh != dummysh);
#endif
    }
    if (splitsh.sh != dummysh) {
      // Split subfaces (and subsegment).
      findedge(&splitsh, pa, pb);
      splitsubedge(newpoint, &splitsh, (queue *) NULL);
    }
  }

  if (b->verbose > 3) {
    for (i = 0; i < wrapcount; i++) {
      //printf("    Updating bots[%i] ", i);
      printtet(&(bots[i]));
      //printf("    Creating newtops[%i] ", i);
      printtet(&(newtops[i]));
    }
  }

  if (flipqueue != (queue *) NULL) {
    for (i = 0; i < wrapcount; i++) {
      enqueueflipface(bots[i], flipqueue);
      enqueueflipface(newtops[i], flipqueue);
    }
  }

  // Set the return handle be avn1n2.  It is got by transforming from
  //   'bots[0]' (which is an1n2v).
  fnext(bots[0], spintet); // spintet is an1vn2.
  esymself(spintet); // spintet is n1avn2.
  enextself(spintet); // spintet is avn1n2.
  *splittet = spintet;

  delete [] bots;
  delete [] newtops;

  return true;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// splitsubedge()    Insert a point on an edge of the surface mesh.          //
//                                                                           //
// The splitting edge is given by 'splitsh'. Assume its three corners are a, //
// b, c, where ab is the edge will be split. ab may be a subsegment.         //
//                                                                           //
// To split edge ab is to split all subfaces conatining ab. If ab is not a   //
// subsegment, there are only two subfaces need be split, otherwise, there   //
// may have any number of subfaces need be split. Each splitting subface abc //
// is shrunk to avc, a new subface vbc is created.  It is important to keep  //
// the orientations of edge rings of avc and vbc be the same as abc's. If ab //
// is a subsegment, it is shrunk to av and a new subsegment vb is created.   //
//                                                                           //
// If there are tetrahedra adjoining to the splitting subfaces, they should  //
// be split before calling this routine, so the connection between the new   //
// tetrahedra and the new subfaces can be correctly set.                     //
//                                                                           //
// On completion, 'splitsh' returns avc.  If 'flipqueue' is not NULL, it     //
// returns all edges which may be non-Delaunay.                              //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::splitsubedge(point newpoint, face* splitsh, queue* flipqueue)
{
  triface abcd, bace, vbcd, bvce;
  face startabc, spinabc, spinsh;
  face oldbc, bccasin, bccasout;
  face ab, bc;
  face avc, vbc, vbc1;
  face av, vb;
  point pa, pb;

  startabc = *splitsh;
  // Is there a subsegment?
  sspivot(startabc, ab);
  if (ab.sh != dummysh) {
    ab.shver = 0; 
    if (sorg(startabc) != sorg(ab)) {
      sesymself(startabc);
    }
  }
  pa = sorg(startabc);
  pb = sdest(startabc);
  
  if (b->verbose > 1) {
    //printf("  Inserting point %d on subedge (%d, %d) %s.\n",
          // pointmark(newpoint), pointmark(pa), pointmark(pb),
           //(ab.sh != dummysh ? "(seg)" : " "));
  }
  
  // Spin arround ab, split every subface containing ab.
  spinabc = startabc;
  do {
    // Adjust spinabc be edge ab.
    if (sorg(spinabc) != pa) {
      sesymself(spinabc);
    }
    // Unmark the face for splitting (used for refinement) 2009-08-17.
    sunmarktest(spinabc);
    // Save old configuration at edge bc, if bc has a subsegment, save the
    //   face link of it and dissolve it from bc.
    senext(spinabc, oldbc);
    spivot(oldbc, bccasout);    
    sspivot(oldbc, bc);
    if (bc.sh != dummysh) {
      if (bccasout.sh != dummysh) {
        // 'spinabc' is not self-bonded.
        spinsh = bccasout;
        do {
          bccasin = spinsh;
          spivotself(spinsh);
        } while (spinsh.sh != oldbc.sh);
      } else {
        bccasout.sh = dummysh;
      }
      ssdissolve(oldbc);
    }
    // Create a new subface.
    makeshellface(subfaces, &vbc);
    // Split abc.
    avc = spinabc;  // Update 'abc' to 'avc'.
    setsdest(avc, newpoint);
    // Make 'vbc' be in the same edge ring as 'avc'. 
    vbc.shver = avc.shver; 
    setsorg(vbc, newpoint); // Set 'vbc'.
    setsdest(vbc, pb);
    setsapex(vbc, sapex(avc));
    if (b->quality && varconstraint) {
      // Copy the area bound into the new subface.
      setareabound(vbc, areabound(avc));
    }
    // Copy the shell marker and shell type into the new subface.
    setshellmark(vbc, shellmark(avc));
    setshelltype(vbc, shelltype(avc));
    if (checkpbcs) {
      // Copy the pbcgroup into the new subface.
      setshellpbcgroup(vbc, shellpbcgroup(avc));
    }
    // Set the connection between updated and new subfaces.
    senext2self(vbc);
    sbond(vbc, oldbc);
    // Set the connection between new subface and casings.
    senext2self(vbc);
    if (bc.sh != dummysh) {
      if (bccasout.sh != dummysh) {
        // Insert 'vbc' into face link.
        sbond1(bccasin, vbc);
        sbond1(vbc, bccasout);
      } else {
        // Bond 'vbc' to itself.
        sdissolve(vbc); // sbond(vbc, vbc);
      }
      ssbond(vbc, bc);
    } else {
      sbond(vbc, bccasout);
    }
    // Go to next subface at edge ab.
    spivotself(spinabc);
    if (spinabc.sh == dummysh) {
      break; // 'ab' is a hull edge.
    }
  } while (spinabc.sh != startabc.sh);

  // Get the new subface vbc above the updated subface avc (= startabc).
  senext(startabc, oldbc);
  spivot(oldbc, vbc);
  if (sorg(vbc) == newpoint) {
    sesymself(vbc);
  }
#ifdef SELF_CHECK
  //assert(sorg(vbc) == sdest(oldbc) && sdest(vbc) == sorg(oldbc));
#endif
  senextself(vbc);
  // Set the face link for the new created subfaces around edge vb.
  spinabc = startabc;
  do {
    // Go to the next subface at edge av.
    spivotself(spinabc);
    if (spinabc.sh == dummysh) {
      break; // 'ab' is a hull edge.
    }
    if (sorg(spinabc) != pa) {
      sesymself(spinabc);
    }
    // Get the new subface vbc1 above the updated subface avc (= spinabc).
    senext(spinabc, oldbc);
    spivot(oldbc, vbc1);
    if (sorg(vbc1) == newpoint) {
      sesymself(vbc1);
    }
#ifdef SELF_CHECK
    //assert(sorg(vbc1) == sdest(oldbc) && sdest(vbc1) == sorg(oldbc));
#endif
    senextself(vbc1);
    // Set the connection: vbc->vbc1.
    sbond1(vbc, vbc1);
    // For the next connection.
    vbc = vbc1;
  } while (spinabc.sh != startabc.sh);

  // Split ab if it is a subsegment.
  if (ab.sh != dummysh) {
    // Unmark the segment for mesh optimization. 2009-08-17.
    sunmarktest(ab);
    // Update subsegment ab to av.
    av = ab;
    setsdest(av, newpoint);
    // Create a new subsegment vb.
    makeshellface(subsegs, &vb);
    setsorg(vb, newpoint);
    setsdest(vb, pb);
    // vb gets the same mark and segment type as av.
    setshellmark(vb, shellmark(av));
    setshelltype(vb, shelltype(av));
    if (b->quality && varconstraint) {
      // Copy the area bound into the new subsegment.
      setareabound(vb, areabound(av));
    }
    // Save the old connection at ab (re-use the handles oldbc, bccasout).
    senext(av, oldbc);
    spivot(oldbc, bccasout);
    // Bond av and vb (bonded at their "fake" edges).
    senext2(vb, bccasin);
    sbond(bccasin, oldbc);
    if (bccasout.sh != dummysh) {
      // There is a subsegment connecting with ab at b. It will connect
      //   to vb at b after splitting.
      bccasout.shver = 0;
      if (sorg(bccasout) != pb) sesymself(bccasout);
#ifdef SELF_CHECK
      //assert(sorg(bccasout) == pb); 
#endif
      senext2self(bccasout);
      senext(vb, bccasin);
      sbond(bccasin, bccasout);
    }
    // Bond all new subfaces (vbc) to vb. 
    spinabc = startabc;
    do {
      // Adjust spinabc be edge av.
      if (sorg(spinabc) != pa) {
        sesymself(spinabc);
      }
      // Get new subface vbc above the updated subface avc (= spinabc).
      senext(spinabc, oldbc);
      spivot(oldbc, vbc);
      if (sorg(vbc) == newpoint) {
        sesymself(vbc);
      }
      senextself(vbc);
      // Bond the new subface and the new subsegment.
      ssbond(vbc, vb);
      // Go to the next.
      spivotself(spinabc);
      if (spinabc.sh == dummysh) {
        break; // There's only one facet at the segment.rr 
      }
    } while (spinabc.sh != startabc.sh);
  }

  // Bond the new subfaces to new tetrahedra if they exist.  New tetrahedra
  //   should have been created before calling this routine.
  spinabc = startabc;
  do {
    // Adjust spinabc be edge av.
    if (sorg(spinabc) != pa) {
      sesymself(spinabc);
    }
    // Get new subface vbc above the updated subface avc (= spinabc).
    senext(spinabc, oldbc);
    spivot(oldbc, vbc);
    if (sorg(vbc) == newpoint) {
      sesymself(vbc);
    }
    senextself(vbc);
    // Get the adjacent tetrahedra at 'spinabc'.
    stpivot(spinabc, abcd);
    if (abcd.tet != dummytet) {
      findedge(&abcd, sorg(spinabc), sdest(spinabc));
      enextfnext(abcd, vbcd);
      fnextself(vbcd);
#ifdef SELF_CHECK
      //assert(vbcd.tet != dummytet);
#endif
      tsbond(vbcd, vbc);
      sym(vbcd, bvce);
      sesymself(vbc);
      tsbond(bvce, vbc);
    } else {
      // One side is empty, check the other side.
      sesymself(spinabc);
      stpivot(spinabc, bace);
      if (bace.tet != dummytet) {
        findedge(&bace, sorg(spinabc), sdest(spinabc));
        enext2fnext(bace, bvce);
        fnextself(bvce);
#ifdef SELF_CHECK
        //assert(bvce.tet != dummytet);
#endif
        sesymself(vbc); 
        tsbond(bvce, vbc);
      }
    }
    // Go to the next.
    spivotself(spinabc);
    if (spinabc.sh == dummysh) {
      break; // 'ab' is a hull edge.
    }
  } while (spinabc.sh != startabc.sh);
  
  if (b->verbose > 3) {
    spinabc = startabc;
    do {
      // Adjust spinabc be edge av.
      if (sorg(spinabc) != pa) {
        sesymself(spinabc);
      }
      //printf("    Updating abc:\n");
      printsh(&spinabc);
      // Get new subface vbc above the updated subface avc (= spinabc).
      senext(spinabc, oldbc);
      spivot(oldbc, vbc);
      if (sorg(vbc) == newpoint) {
        sesymself(vbc);
      }
      senextself(vbc);
      //printf("    Creating vbc:\n");
      printsh(&vbc);
      // Go to the next.
      spivotself(spinabc);
      if (spinabc.sh == dummysh) {
        break; // 'ab' is a hull edge.
      }
    } while (spinabc.sh != startabc.sh);
  }

  if (flipqueue != (queue *) NULL) {
    spinabc = startabc;
    do {
      // Adjust spinabc be edge av.
      if (sorg(spinabc) != pa) {
        sesymself(spinabc);
      }
      senext2(spinabc, oldbc); // Re-use oldbc.
      enqueueflipedge(oldbc, flipqueue);
      // Get new subface vbc above the updated subface avc (= spinabc).
      senext(spinabc, oldbc);
      spivot(oldbc, vbc);
      if (sorg(vbc) == newpoint) {
        sesymself(vbc);
      }
      senextself(vbc);
      senext(vbc, oldbc); // Re-use oldbc.
      enqueueflipedge(oldbc, flipqueue);
      // Go to the next.
      spivotself(spinabc);
      if (spinabc.sh == dummysh) {
        break; // 'ab' is a hull edge.
      }
    } while (spinabc.sh != startabc.sh);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// formstarpolyhedron()    Get the star ployhedron of a point 'pt'.          //
//                                                                           //
// The polyhedron P is formed by faces of tets having 'pt' as a vertex.  If  //
// 'complete' is TRUE, P is the complete star of 'pt'. Otherwise, P is boun- //
// ded by subfaces, i.e. P is only part of the star of 'pt'.                 //
//                                                                           //
// 'tetlist' T returns the tets, it has one of such tets on input. Moreover, //
// if t is in T, then oppo(t) = p.  Topologically, T is the star of p;  and  //
// the faces of T is the link of p. 'verlist' V returns the vertices of T.   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::formstarpolyhedron(point pt, list* tetlist, list* verlist,
  bool complete)
{
  triface starttet, neightet;
  face checksh;
  point ver[3];
  int idx, i, j;

  // Get a tet t containing p.
  starttet = * (triface *)(* tetlist)[0];
  // Let oppo(t) = p.
  for (starttet.loc = 0; starttet.loc < 4; starttet.loc++) {
    if (oppo(starttet) == pt) break;
  }
  //assert(starttet.loc < 4);
  // Add t into T.
  * (triface *)(* tetlist)[0] = starttet;
  infect(starttet);
  if (verlist != (list *) NULL) {
    // Add three verts of t into V.
    ver[0] = org(starttet);
    ver[1] = dest(starttet);
    ver[2] = apex(starttet);
    for (i = 0; i < 3; i++) {
      // Mark the vert by inversing the index of the vert.
      idx = pointmark(ver[i]);
      setpointmark(ver[i], -idx - 1); // -1 to distinguish the zero.
      verlist->append(&(ver[i]));
    }
  }

  // Find other tets by a broadth-first search.
  for (i = 0; i < tetlist->len(); i++) {
    starttet = * (triface *)(* tetlist)[i];
    starttet.ver = 0;
    for (j = 0; j < 3; j++) {
      fnext(starttet, neightet);
      tspivot(neightet, checksh);
      // Should we cross a subface.
      if ((checksh.sh == dummysh) || complete) {
        // Get the neighbor n.
        symself(neightet);
        if ((neightet.tet != dummytet) && !infected(neightet)) {
          // Let oppo(n) = p.
          for (neightet.loc = 0; neightet.loc < 4; neightet.loc++) {
            if (oppo(neightet) == pt) break;
          }
          //assert(neightet.loc < 4);
          // Add n into T.
          infect(neightet);
          tetlist->append(&neightet);
          if (verlist != (list *) NULL) {
            // Add the apex vertex in n into V.
            ver[0] = org(starttet);
            ver[1] = dest(starttet);
            findedge(&neightet, ver[0], ver[1]);
            ver[2] = apex(neightet);
            idx = pointmark(ver[2]);
            if (idx >= 0) {
              setpointmark(ver[2], -idx - 1);
              verlist->append(&(ver[2]));
            }
          }
        }
      }
      enextself(starttet);
    }
  }

  // Uninfect tets.
  for (i = 0; i < tetlist->len(); i++) {
    starttet = * (triface *)(* tetlist)[i];
    uninfect(starttet);
  }
  if (verlist != (list *) NULL) {
    // Uninfect vertices.
    for (i = 0; i < verlist->len(); i++) {
      ver[0] = * (point *)(* verlist)[i];
      idx = pointmark(ver[0]);
      setpointmark(ver[0], -(idx + 1));
    }
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// Terminology: BC(p) and CBC(p), B(p) and C(p).                             //
//                                                                           //
// Given an arbitrary point p,  the Bowyer-Watson cavity BC(p) is formed by  //
// tets whose circumspheres containing p.  The outer faces of BC(p) form a   //
// polyhedron B(p).                                                          //
//                                                                           //
// If p is on a facet F, the constrained Bowyer-Watson cavity CBC(p) on F is //
// formed by subfaces of F whose circumspheres containing p. The outer edges //
// of CBC(p) form a polygon C(p).  B(p) is separated into two parts by C(p), //
// denoted as B_1(p) and B_2(p), one of them may be empty (F is on the hull).//
//                                                                           //
// If p is on a segment S which is shared by n facets.  There exist n C(p)s, //
// each one is a non-closed polygon (without S). B(p) is split into n parts, //
// each of them is denoted as B_i(p), some B_i(p) may be empty.              //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// formbowatcavitysub()    Form CBC(p) and C(p) on a facet F.                //
//                                                                           //
// Parameters: bp = p, bpseg = S, sublist = CBC(p), subceillist = C(p).      //
//                                                                           //
// CBC(p) contains at least one subface on input; S may be NULL which means  //
// that p is inside a facet. On output, all subfaces of CBC(p) are infected, //
// and the edge rings are oriented to the same halfspace.                    //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::formbowatcavitysub(point bp, face* bpseg, list* sublist,
  list* subceillist)
{
  triface adjtet;
  face startsh, neighsh;
  face checkseg;
  point pa, pb, pc, pd;
  REAL sign;
  int i, j;  

  // Form CBC(p) and C(p) by a broadth-first searching.
  for (i = 0; i < sublist->len(); i++) {
    startsh = * (face *)(* sublist)[i]; // startsh = f.
    // Look for three neighbors of f.
    for (j = 0; j < 3; j++) {
      sspivot(startsh, checkseg);
      if (checkseg.sh == dummysh) {
        // Get its neighbor n.
        spivot(startsh, neighsh);
        // Is n already in CBC(p)?
        if (!sinfected(neighsh)) {
          stpivot(neighsh, adjtet);
          if (adjtet.tet == dummytet) {
            sesymself(neighsh);
            stpivot(neighsh, adjtet);
          }
          // For positive orientation that insphere() test requires.
          adjustedgering(adjtet, CW);
          pa = org(adjtet);
          pb = dest(adjtet);
          pc = apex(adjtet);
          pd = oppo(adjtet);
          sign = insphere(pa, pb, pc, pd, bp);
          if (sign >= 0.0) {
            // Orient edge ring of n according to that of f.
            if (sorg(neighsh) != sdest(startsh)) sesymself(neighsh);
            // Collect it into CBC(p).
            sinfect(neighsh);
            sublist->append(&neighsh);
          } else {
            subceillist->append(&startsh); // Found an edge of C(p).
          }
        }
      } else {
        // Do not cross a segment.
        if (bpseg != (face *) NULL) {
          if (checkseg.sh != bpseg->sh) {
            subceillist->append(&startsh); // Found an edge of C(p).
          }
        } else {
          subceillist->append(&startsh); // Found an edge of C(p).
        }
      }
      senextself(startsh);
    }
  }

  if (b->verbose > 2) {
    //printf("    Collect CBC(%d): %d subfaces, %d edges.\n", pointmark(bp),
           //sublist->len(), subceillist->len());
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// formbowatcavityquad()    Form BC_i(p) and B_i(p) in a quadrant.           //
//                                                                           //
// Parameters: bp = p, tetlist = BC_i(p), ceillist = B_i(p).                 //
//                                                                           //
// BC_i(p) contains at least one tet on input. On finish, all tets collected //
// in BC_i(p) are infected. B_i(p) may not closed when p is on segment or in //
// facet. C(p) must be formed before this routine.  Check the infect flag of //
// a subface to identify the unclosed side of B_i(p).  These sides will be   //
// closed by new subfaces of C(p)s.                                          //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::formbowatcavityquad(point bp, list* tetlist, list* ceillist)
{
  triface starttet, neightet;
  face checksh;
  point pa, pb, pc, pd;
  REAL sign;
  int i;

  // Form BC_i(p) and B_i(p) by a broadth-first searching.
  for (i = 0; i < tetlist->len(); i++) {
    starttet = * (triface *)(* tetlist)[i];
    for (starttet.loc = 0; starttet.loc < 4; starttet.loc++) {
      // Try to collect the neighbor of the face (f).
      tspivot(starttet, checksh);
      if (checksh.sh == dummysh) {
        // Get its neighbor n.
        sym(starttet, neightet);
        // Is n already in BC_i(p)?
        if (!infected(neightet)) {
          // For positive orientation that insphere() test requires.
          adjustedgering(neightet, CW);
          pa = org(neightet);
          pb = dest(neightet);
          pc = apex(neightet);
          pd = oppo(neightet);
          sign = insphere(pa, pb, pc, pd, bp);
          if (sign >= 0.0) {
            // Collect it into BC_i(p).
            infect(neightet);
            tetlist->append(&neightet);
          } else {
            ceillist->append(&starttet); // Found a face of B_i(p).
          }
        }
      } else {
        // Do not cross a boundary face.
        if (!sinfected(checksh)) {
          ceillist->append(&starttet); // Found a face of B_i(p).
        }
      }
    }
  }

  if (b->verbose > 2) {
    //printf("    Collect BC_i(%d): %d tets, %d faces.\n", pointmark(bp),
           ///tetlist->len(), ceillist->len());
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// formbowatcavitysegquad()    Form BC_i(p) and B_i(p) in a segment quadrant.//
//                                                                           //
// Parameters: bp = p, tetlist = BC_i(p), ceillist = B_i(p).                 //
//                                                                           //
// BC_i(p) contains at least one tet on input. On finish, all tets collected //
// in BC_i(p) are infected. B_i(p) is not closed. C(p) must be formed before //
// this routine. Check the infect flag of a subface to identify the unclosed //
// sides of B_i(p).  These sides will be closed by new subfaces of C(p)s.    //
//                                                                           //
// During the repair of encroaching subsegments, there may exist locally non-//
// Delaunay faces. These faces are collected in BC_i(p) either.  B_i(p) has  //
// to be formed later than BC_i(p).                                          //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::formbowatcavitysegquad(point bp, list* tetlist,list* ceillist)
{
  triface starttet, neightet, cavtet;
  face checksh;
  point pa, pb, pc, pd, pe;
  REAL sign;
  int i;

  // Form BC_i(p) by a broadth-first searching.
  for (i = 0; i < tetlist->len(); i++) {
    starttet = * (triface *)(* tetlist)[i];
    for (starttet.loc = 0; starttet.loc < 4; starttet.loc++) {
      // Try to collect the neighbor of the face f.
      tspivot(starttet, checksh);
      if (checksh.sh == dummysh) {
        // Get its neighbor n.
        sym(starttet, neightet);
        // Is n already in BC_i(p)?
        if (!infected(neightet)) {
          // For positive orientation that insphere() test requires.
          adjustedgering(neightet, CW);
          pa = org(neightet);
          pb = dest(neightet);
          pc = apex(neightet);
          pd = oppo(neightet);
          sign = insphere(pa, pb, pc, pd, bp);
          if (sign >= 0.0) {
            // Collect it into BC_i(p).
            infect(neightet);
            tetlist->append(&neightet);
          } else {
            // Check if the face is locally non-Delaunay.
            pe = oppo(starttet);
            sign = insphere(pa, pb, pc, pd, pe);
            if (sign >= 0.0) {
              // Collect it into BC_i(p).
              infect(neightet);
              tetlist->append(&neightet);
            }
          }
        }
      }
    }
  }

  // Generate B_i(p).
  for (i = 0; i < tetlist->len(); i++) {
    cavtet = * (triface *)(* tetlist)[i];
    for (cavtet.loc = 0; cavtet.loc < 4; cavtet.loc++) {
      tspivot(cavtet, checksh);
      if (checksh.sh == dummysh) {
        sym(cavtet, neightet);
        if (!infected(neightet)) {
          ceillist->append(&cavtet); // Found a face of B(p).
        }
      } else {
        // Do not cross a boundary face.
        if (!sinfected(checksh)) {
          ceillist->append(&cavtet); // Found a face of B(p).
        }
      }
    }
  }

  if (b->verbose > 2) {
    //printf("    Collect BC_i(%d): %d tets, %d faces.\n", pointmark(bp),
           //tetlist->len(), ceillist->len());
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// formbowatcavity()    Form BC(p), B(p), CBC(p)s, and C(p)s.                //
//                                                                           //
// If 'bpseg'(S) != NULL, p is on segment S, else, p is on facet containing  //
// 'bpsh' (F).  'n' returns the number of quadrants in BC(p). 'nmax' is the  //
// maximum pre-allocated array length for the lists.                         //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::formbowatcavity(point bp, face* bpseg, face* bpsh, int* n,
  int* nmax, list** sublists, list** subceillists, list** tetlists,
  list** ceillists)
{
  list *sublist;
  triface adjtet;
  face startsh, spinsh;
  point pa, pb;
  int i, j;

  *n = 0;
  if (bpseg != (face *) NULL) {
    // p is on segment S.
    bpseg->shver = 0;
    pa = sorg(*bpseg);
    pb = sdest(*bpseg);
    // Count the number of facets sharing at S.
    spivot(*bpseg, startsh);
    spinsh = startsh;
    do {
      (*n)++; // spinshlist->append(&spinsh);
      spivotself(spinsh);
    } while (spinsh.sh != startsh.sh);
    // *n is the number of quadrants around S.
    if (*n > *nmax) {
      // Reallocate arrays. Should not happen very often.
      delete [] tetlists;
      delete [] ceillists;
      delete [] sublists;
      delete [] subceillists;
      tetlists = new list*[*n];
      ceillists = new list*[*n];
      sublists = new list*[*n];
      subceillists = new list*[*n];
      *nmax = *n;
    }
    // Form CBC(p)s and C(p)s.
    spinsh = startsh;
    for (i = 0; i < *n; i++) {
      sublists[i] = new list(sizeof(face), NULL, 256);
      subceillists[i] = new list(sizeof(face), NULL, 256);
      // Set a subface f to start search.
      startsh = spinsh;
      // Let f face to the quadrant of interest (used in forming BC(p)).
      findedge(&startsh, pa, pb);
      sinfect(startsh);
      sublists[i]->append(&startsh);
      formbowatcavitysub(bp, bpseg, sublists[i], subceillists[i]);
      // Go to the next facet.
      spivotself(spinsh);
    }
  } else if (sublists != (list **) NULL) {
    // p is on a facet.
    *n = 2;
    // Form CBC(p) and C(p).
    sublists[0] = new list(sizeof(face), NULL, 256);
    subceillists[0] = new list(sizeof(face), NULL, 256);
    sinfect(*bpsh);
    sublists[0]->append(bpsh);
    formbowatcavitysub(bp, NULL, sublists[0], subceillists[0]);
  } else {
    // p is inside a tet.
    *n = 1;
  }

  // Form BC_i(p) and B_i(p).
  for (i = 0; i < *n; i++) {
    tetlists[i] = new list(sizeof(triface), NULL, 256);
    ceillists[i] = new list(sizeof(triface), NULL, 256);
    if (sublists != (list **) NULL) {
      // There are C(p)s.
      sublist = ((bpseg == (face *) NULL) ? sublists[0] : sublists[i]);
      // Add all adjacent tets of C_i(p) into BC_i(p).
      for (j = 0; j < sublist->len(); j++) {    
        startsh = * (face *)(* sublist)[j];
        // Adjust the side facing to the right quadrant for C(p).
        if ((bpseg == (face *) NULL) && (i == 1)) sesymself(startsh);
        stpivot(startsh, adjtet);
        if (adjtet.tet != dummytet) {
          if (!infected(adjtet)) {
            infect(adjtet);
            tetlists[i]->append(&adjtet);
          }
        }
      }
      if (bpseg != (face *) NULL) {
        // The quadrant is bounded by another facet.
        sublist = ((i < *n - 1) ? sublists[i + 1] : sublists[0]);
        for (j = 0; j < sublist->len(); j++) {    
          startsh = * (face *)(* sublist)[j];
          // Adjust the side facing to the right quadrant for C(p).
          sesymself(startsh);
          stpivot(startsh, adjtet);
          if (adjtet.tet != dummytet) {
            if (!infected(adjtet)) {
              infect(adjtet);
              tetlists[i]->append(&adjtet);
            }
          }
        }
      }
    }
    // It is possible that BC_i(p) is empty.
    if (tetlists[i]->len() == 0) continue;
    // Collect the rest of tets of BC_i(p) and form B_i(p).
    // if (b->conformdel) {
      // formbowatcavitysegquad(bp, tetlists[i], ceillists[i]);
    // } else {
      formbowatcavityquad(bp, tetlists[i], ceillists[i]);
    // }
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// releasebowatcavity()    Undo and free the memory allocated in routine     //
//                         formbowatcavity().                                //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::releasebowatcavity(face* bpseg, int n, list** sublists,
  list** subceillist, list** tetlists, list** ceillists)
{
  triface oldtet;
  face oldsh;
  int i, j;

  if (sublists != (list **) NULL) {
    // Release CBC(p)s.
    for (i = 0; i < n; i++) {
      // Uninfect subfaces of CBC(p).
      for (j = 0; j < sublists[i]->len(); j++) {
        oldsh = * (face *)(* (sublists[i]))[j];
#ifdef SELF_CHECK
        //assert(sinfected(oldsh));
#endif
        suninfect(oldsh);
      }
      delete sublists[i];
      delete subceillist[i];
      sublists[i] = (list *) NULL;
      subceillist[i] = (list *) NULL;
      if (bpseg == (face *) NULL) break;
    }
  }
  // Release BC(p).
  for (i = 0; i < n; i++) {
    // Uninfect tets of BC_i(p).
    for (j = 0; j < tetlists[i]->len(); j++) {
      oldtet = * (triface *)(* (tetlists[i]))[j];
#ifdef SELF_CHECK
      //assert(infected(oldtet));
#endif
      uninfect(oldtet);
    }
    delete tetlists[i];
    delete ceillists[i];
    tetlists[i] = (list *) NULL;
    ceillists[i] = (list *) NULL;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// validatebowatcavityquad()    Valid B_i(p).                                //
//                                                                           //
// B_i(p) is valid if all faces of B_i(p) are visible by p, else B_i(p) is   //
// invalid.  Each tet of BC_i(p) which has such a face is marked (uninfect). //
// They will be removed in updatebowatcavityquad().                          //
//                                                                           //
// Return TRUE if B(p) is valid, else, return FALSE.                         //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::validatebowatcavityquad(point bp,list* ceillist,REAL maxcosd)
{
  triface ceiltet;
  point pa, pb, pc;
  REAL ori, cosd;
  int remcount, i;

  // Check the validate of B(p), cut tets having invisible faces.
  remcount = 0;
  for (i = 0; i < ceillist->len(); i++) {
    ceiltet = * (triface *)(* ceillist)[i];
    if (infected(ceiltet)) {
      adjustedgering(ceiltet, CCW);
      pa = org(ceiltet);
      pb = dest(ceiltet);
      pc = apex(ceiltet);
      ori = orient3d(pa, pb, pc, bp);
      if (ori >= 0.0) {
        // Found an invisible face.
        uninfect(ceiltet);
        remcount++;
        continue;
      }
      // If a non-trival 'maxcosd' is given.
      if (maxcosd > -1.0) {
        // Get the maximal dihedral angle of tet abcp.
        tetalldihedral(pa, pb, pc, bp, NULL, &cosd, NULL);
        // Do not form the tet if the maximal dihedral angle is not reduced.
        if (cosd < maxcosd) {
          uninfect(ceiltet);
          remcount++;
        }
      }
    }
  }
  return remcount == 0;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// updatebowatcavityquad()    Update BC_i(p) and reform B_i(p).              //
//                                                                           //
// B_i(p) is invalid and some tets in BC_i(p) have been marked to be removed //
// in validatebowatcavityquad().  This routine actually remove the cut tets  //
// of BC_i(p) and re-form the B_i(p).                                        //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::updatebowatcavityquad(list* tetlist, list* ceillist)
{
  triface cavtet, neightet;
  face checksh;
  int remcount, i;

  remcount = 0;
  for (i = 0; i < tetlist->len(); i++) {
    cavtet = * (triface *)(* tetlist)[i];
    if (!infected(cavtet)) {
      tetlist->del(i, 1);
      remcount++;
      i--;
    }
  }

  // Are there tets have been cut in BC_i(p)?
  if (remcount > 0) {
    // Re-form B_i(p).
    ceillist->clear();
    for (i = 0; i < tetlist->len(); i++) {
      cavtet = * (triface *)(* tetlist)[i];
      for (cavtet.loc = 0; cavtet.loc < 4; cavtet.loc++) {
        tspivot(cavtet, checksh);
        if (checksh.sh == dummysh) {
          sym(cavtet, neightet);
          if (!infected(neightet)) {
            ceillist->append(&cavtet); // Found a face of B_i(p).
          }
        } else {
          // Do not cross a boundary face.
          if (!sinfected(checksh)) {
            ceillist->append(&cavtet); // Found a face of B_i(p).
          }
        }
      }
    }
    if (b->verbose > 2) {
      //printf("    Update BC_i(p): %d tets, %d faces.\n", tetlist->len(),
             //ceillist->len());
    }
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// updatebowatcavitysub()    Check and update CBC(p) and C(p).               //
//                                                                           //
// A CBC(p) is valid if all its subfaces are inside or on the hull of BC(p). //
// A subface s of CBC(p) is invalid if it is in one of the two cases:        //
//   (1) s is completely outside BC(p);                                      //
//   (2) s has two adjacent tets but only one of them is in BC(p);           //
// s is removed from CBC(p) if it is invalid. If there is an adjacent tet of //
// s which is in BC(p), it gets removed from BC(p) too. If CBC(p) is updated,//
// C(p) is re-formed.                                                        //
//                                                                           //
// A C(p) is valid if all its edges are on the hull of BC(p).  An edge e of  //
// C(p) may be inside BC(p) if e is a segment and belongs to only one facet. //
// To correct C(p), a tet of BC(p) which shields e gets removed.             //
//                                                                           //
// If BC(p) is formed with locally non-Delaunay check (b->conformdel > 0).   //
// A boundary-consistent check is needed for non-segment edges of C(p). Let  //
// e be such an edge, the subface f contains e and outside C(p) may belong   //
// to B(p) due to the non-coplanarity of the facet definition.  The tet of   //
// BC(p) containing f gets removed to avoid creating a degenerate new tet.   //
//                                                                           //
// 'cutcount' accumulates the total number of cuttets(not only by this call).//
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::updatebowatcavitysub(list* sublist, list* subceillist,
  int* cutcount)
{
  triface adjtet, rotface;
  face checksh, neighsh;
  face checkseg;
  point pa, pb, pc;
  REAL ori1, ori2;
  int remcount;
  int i, j;

  remcount = 0;
  // Check the validity of CBC(p).
  for (i = 0; i < sublist->len(); i++) {
    checksh = * (face *)(* sublist)[i];
    // Check two adjacent tets of s.
    for (j = 0; j < 2; j++) {
      stpivot(checksh, adjtet);
      if (adjtet.tet != dummytet) {
        if (!infected(adjtet)) {
          // Could be either case (1) or (2).
          suninfect(checksh); // s survives.
          // If the sym. adjtet exists, it should remove from BC(p) too.
          sesymself(checksh);
          stpivot(checksh, adjtet);
          if (adjtet.tet != dummytet) {
            if (infected(adjtet)) {
              // Found an adj. tet in BC(p), remove it.
              uninfect(adjtet);
              (*cutcount)++;
            }
          }
          // Remove s from C(p).
          sublist->del(i, 1);
          i--;
          remcount++;
          break;
        }
      }
      sesymself(checksh);
    }
  }
  if (remcount > 0) {
    // Some subfaces have been removed from the cavity.
    if (checkpbcs) {
      // Check if the facet has a PBC defined. 
      checksh = * (face *)(* sublist)[0];
      if (shellpbcgroup(checksh) >= 0) {
        // Yes, A PBC facet. Remove all subfaces -- Do not insert the point.
        for (i = 0; i < sublist->len(); i++) {
          checksh = * (face *)(* sublist)[i];
          suninfect(checksh);
          // Remove both side tets from the cavity.
          for (j = 0; j < 2; j++) {
            stpivot(checksh, adjtet);
            if (adjtet.tet != dummytet) {
              if (infected(adjtet)) {
                uninfect(adjtet);
                (*cutcount)++;
              }
            }
            sesymself(checksh);
          }
        }
        remcount += sublist->len();
        sublist->clear();
      }
    }
    if (b->verbose > 2) {
      //printf("    Removed %d subfaces from CBC(p).\n", remcount);
    }
    // Re-generate C(p).
    subceillist->clear();
    for (i = 0; i < sublist->len(); i++) {
      checksh = * (face *)(* sublist)[i];
      for (j = 0; j < 3; j++) {
        spivot(checksh, neighsh);
        if (!sinfected(neighsh)) {
          subceillist->append(&checksh);
        }
        senextself(checksh);
      }
    }
    if (b->verbose > 2) {
      //printf("    Update CBC(p): %d subs, %d edges.\n", sublist->len(),
             //subceillist->len());
    }
  }

  // Check the validity of C(p).
  for (i = 0; i < subceillist->len(); i++) {
    checksh = * (face *)(* subceillist)[i];
    sspivot(checksh, checkseg);
    if (checkseg.sh != dummysh) {
      // A segment. Check if it is inside BC(p).
      stpivot(checksh, adjtet);
      if (adjtet.tet == dummytet) {
        sesym(checksh, neighsh);
        stpivot(neighsh, adjtet);
      }
      findedge(&adjtet, sorg(checkseg), sdest(checkseg));
      adjustedgering(adjtet, CCW);
      fnext(adjtet, rotface); // It's the same tet.
      // Rotate rotface (f), stop on either of the following cases:
      //   (a) meet a subface, or
      //   (b) enter an uninfected tet, or
      //   (c) rewind back to adjtet.
      do {
        if (!infected(rotface)) break; // case (b)
        tspivot(rotface, neighsh);
        if (neighsh.sh != dummysh) break; // case (a)
        // Go to the next tet of the facing ring.
        fnextself(rotface);
      } while (apex(rotface) != apex(adjtet));
      // Is it case (c)?
      if (apex(rotface) == apex(adjtet)) {
        // The segment is enclosed by BC(p), invalid cavity.
        pa = org(adjtet);
        pb = dest(adjtet);
        pc = apex(adjtet);
        // Find the shield tet and cut it. Notice that the shield tet may
        //   not be unique when there are four coplanar points, ie.,
        //   ori1 * ori2 == 0.0. In such case, choose either of them.
        fnext(adjtet, rotface);
        do {
          fnextself(rotface);
          //assert(infected(rotface));
          ori1 = orient3d(pa, pb, pc, apex(rotface));
          ori2 = orient3d(pa, pb, pc, oppo(rotface));
        } while (ori1 * ori2 > 0.0);
        // Cut this tet from BC(p).
        uninfect(rotface);
        (*cutcount)++;
      }
    } else {
      /*// An edge. Check if boundary-consistency should be enforced.
      if (b->conformdel > 0) {
        // Get the adj-sub n at e, it must be outside C(p).
        spivot(checksh, neighsh);
        //assert(!sinfected(neighsh));
        // Check if n is on B(p).
        for (j = 0; j < 2; j++) {
          stpivot(neighsh, adjtet);
          if (adjtet.tet != dummytet) {
            if (infected(adjtet)) {
              uninfect(adjtet);
              (*cutcount)++;
            }
          }
          sesymself(neighsh);
        }
      } */
    }
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// trimbowatcavity()    Validate B(p), CBC(p)s and C(p)s, update BC(p).      //
//                                                                           //
// A B(p) is valid if all its faces are visible by p. If a face f of B(p) is //
// found invisible by p, the tet of BC(p) containing f gets removed and B(p) //
// is refromed. The new B(p) may still contain invisible faces by p. Iterat- //
// ively do the above procedure until B(p) is satisfied.                     //
//                                                                           //
// A CBC(p) is valid if each subface of CBC(p) is either on the hull of BC(p)//
// or completely inside BC(p). If a subface s of CBC(p) is not valid, it is  //
// removed from CBC(p) and C(p) is reformed. If there exists a tet t of BC(p)//
// containg s, t is removed from BC(p). The process for validating BC(p) and //
// B(p) is re-excuted.                                                       //
//                                                                           //
// A C(p) is valid if each edge of C(p) is on the hull of BC(p). If an edge  //
// e of C(p) is invalid (e should be a subsegment which only belong to one   //
// facet), a tet of BC(p) which contains e and has two other faces shielding //
// e is removed. The process for validating BC(p) and B(p) is re-excuted.    //
//                                                                           //
// If either BC(p) or CBC(p) becomes empty. No valid BC(p) is found, return  //
// FALSE. else, return TRUE.                                                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::trimbowatcavity(point bp, face* bpseg, int n, list** sublists,
  list** subceillists, list** tetlists, list** ceillists, REAL maxcosd)
{
  bool valflag;
  int oldnum, cutnum, cutcount;
  int i;

  cutnum = 0; // Count the total number of cut-off tets of BC(p).
  valflag = true;

  do {
    // Validate BC(p), B(p).
    for (i = 0; i < n && valflag; i++) {
      oldnum = tetlists[i]->len();
      // Iteratively validate BC_i(p) and B_i(p).
      while (!validatebowatcavityquad(bp, ceillists[i], maxcosd)) {
        // Update BC_i(p) and B_i(p).
        updatebowatcavityquad(tetlists[i], ceillists[i]);
        valflag = tetlists[i]->len() > 0;
      }
      cutnum += (oldnum - tetlists[i]->len());
    }
    if (valflag && (sublists != (list **) NULL)) {
      // Validate CBC(p), C(p).
      cutcount = 0;
      for (i = 0; i < n; i++) {
        updatebowatcavitysub(sublists[i], subceillists[i], &cutcount);
        // Only do once if p is on a facet.
        if (bpseg == (face *) NULL) break; 
      }
      // Are there cut tets?
      if (cutcount > 0) {
        // Squeeze all cut tets in BC(p), keep valflag once it gets FLASE.
        for (i = 0; i < n; i++) {
          if (tetlists[i]->len() > 0) {
            updatebowatcavityquad(tetlists[i], ceillists[i]);
            if (valflag) {
              valflag = tetlists[i]->len() > 0;
            }
          }
        }
        cutnum += cutcount;
        // Go back to valid the updated BC(p).
        continue;
      }
    }
    break; // Leave the while-loop.
  } while (true);

  // Check if any CBC(p) becomes non-empty.
  if (valflag && (sublists != (list **) NULL)) {
    for (i = 0; i < n && valflag; i++) {
      valflag = (sublists[i]->len() > 0);
      if (bpseg == (face *) NULL) break; 
    }
  }

  if (valflag && (cutnum > 0)) {
    // Accumulate counters.
    if (bpseg != (face *) NULL) {
      updsegcount++;
    } else if (sublists != (list **) NULL) {
      updsubcount++;
    } else {
      updvolcount++;
    }
  }

  if (!valflag) {
    // Accumulate counters.
    if (bpseg != (face *) NULL) {
      failsegcount++;
    } else if (sublists != (list **) NULL) {
      failsubcount++;
    } else {
      failvolcount++;
    }
  }

  return valflag;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// bowatinsertsite()    Insert a point using the Bowyer-Watson method.       //
//                                                                           //
// Parameters: 'bp' = p, 'splitseg' = S, 'n' = the number of quadrants,      //
// 'sublists', an array of CBC_i(p)s, 'subceillists', an array of C_i(p)s,   //
// 'tetlists', an array of BC_i(p)s, 'ceillists', an array of B_i(p)s.       //
//                                                                           //
// If p is inside the mesh domain, then S = NULL, n = 1, CBC(p) and C(p) are //
//   NULLs. 'tetlists[0]' = BC(p), 'ceillists[0]' = B(p).                    //
// If p is on a facet F, then S = NULL, n = 2, and 'subceillists[0]' = C(p), //
//  'subceillists[1]' is not needed (set it to NULL). B_1(p) and B_2(p) are  //
//  in 'ceillists[0]' and 'ceillists[1]'.                                    //
// If p is on a segment S, then F(S) is a list of subfaces around S, and n = //
//   len(F(S)), there are n C_i(p)s and B_i(p)s supplied in 'subceillists[i]'//
//   and 'ceillists[i]'.                                                     //
//                                                                           //
// If 'verlist' != NULL, it returns a list of vertices which connect to p.   //
//   This vertices are used for interpolating size of p.                     //
//                                                                           //
// If 'flipque' != NULL, it returns a list of internal faces of new tets in  //
//   BC(p), faces on C(p)s are excluded. These faces may be locally non-     //
//   Delaunay and will be flipped if they are flippable. Such non-Delaunay   //
//   faces may exist when p is inserted to split an encroaching segment.     //
//                                                                           //
// 'chkencseg', 'chkencsub', and 'chkbadtet' are flags that indicate whether //
// or not there should be checks for the creation of encroached subsegments, //
// subfaces, or bad quality tets. If 'chkencseg' = TRUE, the encroached sub- //
// segments are added to the list of subsegments to be split.                //
//                                                                           //
// On return, 'ceillists' returns Star(p).                                   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::bowatinsertsite(point bp,face* splitseg,int n,list** sublists,
  list** subceillists, list** tetlists, list** ceillists, list* verlist,
  queue* flipque, bool chkencseg, bool chkencsub, bool chkbadtet)
{
  list *ceillist, *subceillist; 
  triface oldtet, newtet, newface, rotface, neightet; 
  face oldsh, newsh, newedge, checksh;
  face spinsh, casingin, casingout;
  face *apsegshs, *pbsegshs;
  face apseg, pbseg, checkseg;
  point pa, pb, pc;
  REAL attrib, volume;
  int idx, i, j, k;

  if (b->verbose > 1) {
    //printf("    Insert point %d (%.12g, %.12g, %.12g)", pointmark(bp), bp[0],
          // bp[1], bp[2]);
  }
  if (splitseg != (face *) NULL) {
    if (b->verbose > 1) {
      //printf(" on segment.\n");
    }
    bowatsegcount++;
  } else {
    if (subceillists != (list **) NULL) {
      if (b->verbose > 1) {
        //printf(" on facet.\n");
      }
      bowatsubcount++;
    } else {
      if (b->verbose > 1) {
        //printf(" in volume.\n");
      }
      bowatvolcount++;
    }
  }

  // Create new tets to fill B(p).
  for (k = 0; k < n; k++) {
    // Create new tets from each B_i(p).
    ceillist = ceillists[k];
    for (i = 0; i < ceillist->len(); i++) {
      oldtet = * (triface *)(* ceillist)[i];
      adjustedgering(oldtet, CCW);
      pa = org(oldtet);
      pb = dest(oldtet);
      pc = apex(oldtet);
      maketetrahedron(&newtet);
      setorg(newtet, pa);
      setdest(newtet, pb);
      setapex(newtet, pc);
      setoppo(newtet, bp);
      for (j = 0; j < in->numberoftetrahedronattributes; j++) {
        attrib = elemattribute(oldtet.tet, j);
        setelemattribute(newtet.tet, j, attrib);
      }
      if (b->varvolume) {
        volume = volumebound(oldtet.tet);
        if (volume > 0.0) {
          if (!b->fixedvolume && b->refine) {
            // '-r -a' switches and a .vol file case. Enlarge the maximum
            //   volume constraint for the new tets. Hence the new points
            //   only spread near the original constrained tet.
            volume *= 1.2;
          }
        }
        setvolumebound(newtet.tet, volume);
      }
      sym(oldtet, neightet);
      tspivot(oldtet, checksh);
      if (neightet.tet != dummytet) {
        bond(newtet, neightet);
      }
      if (checksh.sh != dummysh) {
        tsbond(newtet, checksh);
      }
      if (verlist != (list *) NULL) {
        // Collect vertices connecting to p.
        idx = pointmark(pa);
        if (idx >= 0) {
          setpointmark(pa, -idx - 1);
          verlist->append(&pa);
        }
        idx = pointmark(pb);
        if (idx >= 0) {
          setpointmark(pb, -idx - 1);
          verlist->append(&pb);
        }
        idx = pointmark(pc);
        if (idx >= 0) {
          setpointmark(pc, -idx - 1);
          verlist->append(&pc);
        }
      }
      // Replace the tet by the newtet for checking the quality.
      * (triface *)(* ceillist)[i] = newtet;
    }
  }
  if (verlist != (list *) NULL) {
    // Uninfect collected vertices.
    for (i = 0; i < verlist->len(); i++) {
      pa = * (point *)(* verlist)[i];
      idx = pointmark(pa);
      setpointmark(pa, -(idx + 1));
    }
  }

  // Connect new tets of B(p). Not all faces of new tets can be connected,
  //   e.g., if there are empty B_i(p)s.
  for (k = 0; k < n; k++) {
    ceillist = ceillists[k];
    for (i = 0; i < ceillist->len(); i++) {
      newtet = * (triface *)(* ceillist)[i];
      newtet.ver = 0;
      for (j = 0; j < 3; j++) {
        fnext(newtet, newface);
        sym(newface, neightet);
        if (neightet.tet == dummytet) {
          // Find the neighbor face by rotating the faces at edge ab.
          esym(newtet, rotface);
          pa = org(rotface);
          pb = dest(rotface);
          while (fnextself(rotface));
          // Do we meet a boundary face?
          tspivot(rotface, checksh);
          if (checksh.sh != dummysh) {
            // Walk through the boundary and continue to rotate faces.
            do {
              findedge(&checksh, pa, pb);
              sfnextself(checksh);
              //assert((sorg(checksh) == pa) && (sdest(checksh) == pb));
              stpivot(checksh, rotface);
              if (infected(rotface)) {
                // Meet an old tet of B_i(p). This side is on the hull and
                //   will be connected to a new subface created in C(p).
                break;
              }
              findedge(&rotface, pa, pb);
              while (fnextself(rotface));
              tspivot(rotface, checksh);
            } while (checksh.sh != dummysh);
          }
          // The rotface has edge ab, but it may not have newpt.
          if (apex(rotface) == apex(newface)) { 
            // Bond the two tets together.
            bond(newface, rotface);
            // Queue (uniquely) this face if 'flipque' is given.
            if (flipque != (queue *) NULL) {
              enqueueflipface(newface, flipque);
            }
          }
        }
        enextself(newtet);
      }
    }
  }

  if (subceillists != (list **) NULL) {
    // There are C(p)s.
    if (splitseg != (face *) NULL) {
      // S (ab) is split by p.
      splitseg->shver = 0;
      pa = sorg(*splitseg);
      pb = sdest(*splitseg);
      // Allcate two arrays for saving the subface rings of the two new
      //   segments a->p and p->b.
      apsegshs = new face[n];
      pbsegshs = new face[n];
    }

    // For each C_k(p), do the following:
    //   (1) Create new subfaces to fill C_k(p), insert them into B(p);
    //   (2) Connect new subfaces to each other;
    for (k = 0; k < n; k++) {      
      subceillist = subceillists[k];

      // Check if 'hullsize' should be updated.
      oldsh = * (face *)(* subceillist)[0];
      stpivot(oldsh, neightet);
      if (neightet.tet != dummytet) {
        sesymself(oldsh);
        stpivot(oldsh, neightet);
      }
      if (neightet.tet == dummytet) {
        // The hull size changes.
        hullsize += (subceillist->len() - sublists[k]->len());
      }

      // (1) Create new subfaces to fill C_k(p), insert them into B(p).
      for (i = 0; i < subceillist->len(); i++) {
        oldsh = * (face *)(* subceillist)[i];
        makeshellface(subfaces, &newsh);
        setsorg(newsh, sorg(oldsh));
        setsdest(newsh, sdest(oldsh));
        setsapex(newsh, bp);
        if (b->quality && varconstraint) {
          setareabound(newsh, areabound(oldsh));
        }
        setshellmark(newsh, shellmark(oldsh));
        setshelltype(newsh, shelltype(oldsh));
        if (checkpbcs) {
          setshellpbcgroup(newsh, shellpbcgroup(oldsh));
        }
        // Replace oldsh by newsh at the edge.
        spivot(oldsh, casingout);
        sspivot(oldsh, checkseg);
        if (checkseg.sh != dummysh) {
          // A segment. Insert s into the face ring, ie, s_in -> s -> s_out.
          if (casingout.sh != dummysh) { // if (oldsh.sh != casingout.sh) {
            // s is not bonded to itself.
            spinsh = casingout;
            do {
              casingin = spinsh;
              spivotself(spinsh);
            } while (sapex(spinsh) != sapex(oldsh));
            //assert(casingin.sh != oldsh.sh); 
            // Bond s_in -> s -> s_out (and dissolve s_in -> s_old -> s_out).
            sbond1(casingin, newsh);
            sbond1(newsh, casingout);
          } else {
            // Bond newsh -> newsh.
            sdissolve(newsh); // sbond(newsh, newsh);
          }
          // Bond the segment.
          ssbond(newsh, checkseg);
        } else {
          // Bond s <-> s_out (and dissolve s_out -> s_old).
          sbond(newsh, casingout);
        }

        // Insert newsh into B(p). Use the coonections of oldsh.
        stpivot(oldsh, neightet);
        if (neightet.tet == dummytet) {
          sesymself(oldsh);
          sesymself(newsh); // Keep the same orientation as oldsh.
          stpivot(oldsh, neightet);
        }
        //assert(infected(neightet));
        // Set on the rotating edge.
        findedge(&neightet, sorg(oldsh), sdest(oldsh));
        // Choose the rotating direction (to the inside of B(p)).
        adjustedgering(neightet, CCW);
        rotface = neightet;
        // Rotate face. Stop at a non-infected tet t (not in B(p)) or a
        //   hull face f (on B(p)). Get the neighbor n of t or f.  n is
        //   a new tet that has just been created to fill B(p).
        do {
          fnextself(rotface);
          sym(rotface, neightet);
          if (neightet.tet == dummytet) {
            tspivot(rotface, checksh);
            //assert(checksh.sh != dummysh);
            stpivot(checksh, newtet);
            break;
          } else if (!infected(neightet)) {
            sym(neightet, newtet);
            break;
          }
        } while (true);
        //assert(newtet.tet != rotface.tet);
        // Set the rotating edge of n.
        findedge(&newtet, sorg(oldsh), sdest(oldsh));
        // Choose the rotating direction (to the inside of B(p)).
        adjustedgering(newtet, CCW);
        fnext(newtet, newface);
        //assert(apex(newface) == bp);
        // newsh has already been oriented toward n.
        tsbond(newface, newsh);
        sym(newface, neightet); // 'neightet' maybe outside.
        sesymself(newsh);
        tsbond(neightet, newsh); // Bond them anyway.

        // Replace oldsh by newsh in list.
        * (face *)(* subceillist)[i] = newsh;
      }

      // (2) Connect new subfaces to each other.
      for (i = 0; i < subceillist->len(); i++) {
        // Get a face cdp.
        newsh = * (face *)(* subceillist)[i];
        // Get a new tet containing cdp.
        stpivot(newsh, newtet);
        if (newtet.tet == dummytet) {
          sesymself(newsh);
          stpivot(newsh, newtet);
        }
        for (j = 0; j < 2; j++) {
          if (j == 0) {
            senext(newsh, newedge); // edge dp.
          } else {
            senext2(newsh, newedge); // edge pc.
            sesymself(newedge); // edge cp.
          }
          if (splitseg != (face *) NULL) {
            // Don not operate on newedge if it is ap or pb.
            if (sorg(newedge) == pa) {
              apsegshs[k] = newedge;
              continue;
            } else if (sorg(newedge) == pb) {
              pbsegshs[k] = newedge;
              continue;
            }
          }
          // There should no segment inside the cavity. Check it.
          sspivot(newedge, checkseg);
          //assert(checkseg.sh == dummysh);
          spivot(newedge, casingout);
          if (casingout.sh == dummysh) {
            rotface = newtet;
            findedge(&rotface, sorg(newedge), sdest(newedge));
            // Rotate newtet until meeting a new subface which contains
            //   newedge. It must exist since newedge is not a seg.
            adjustedgering(rotface, CCW);
            do {
              fnextself(rotface);
              tspivot(rotface, checksh);
              if (checksh.sh != dummysh) break;
            } while (true);
            findedge(&checksh, sorg(newedge), sdest(newedge));
            sbond(newedge, checksh);
          }
        }
      }
      // Only do once if p is on a facet.
      if (splitseg == (face *) NULL) break;
    } // for (k = 0; k < n; k++)

    if (splitseg != (face *) NULL) {
      // Update a->b to be a->p.
      apseg = *splitseg;
      setsdest(apseg, bp);
      // Create a new subsegment p->b.
      makeshellface(subsegs, &pbseg);
      setsorg(pbseg, bp);
      setsdest(pbseg, pb);
      // p->b gets the same mark and segment type as a->p.
      setshellmark(pbseg, shellmark(apseg));
      setshelltype(pbseg, shelltype(apseg));
      if (b->quality && varconstraint) {
        // Copy the area bound into the new subsegment.
        setareabound(pbseg, areabound(apseg));
      }
      senext(apseg, checkseg);
      // Get the old connection at b of a->b.
      spivot(checkseg, casingout);
      // Bond a->p and p->b together.
      senext2(pbseg, casingin);
      sbond(casingin, checkseg);
      if (casingout.sh != dummysh) {
        // There is a subsegment connect at b of p->b.
        casingout.shver = 0;
#ifdef SELF_CHECK
        //assert(sorg(casingout) == pb); 
#endif
        senext2self(casingout);
        senext(pbseg, casingin);
        sbond(casingin, casingout);
      }

      // Bond all new subfaces to a->p and p->b.
      for (i = 0; i < n; i++) {
        spinsh = apsegshs[i];
        findedge(&spinsh, pa, bp);
        ssbond(spinsh, apseg);
        spinsh = pbsegshs[i];
        findedge(&spinsh, bp, pb);
        ssbond(spinsh, pbseg);
      }
      // Bond all subfaces share at a->p together.
      for (i = 0; i < n; i++) {
        spinsh = apsegshs[i];
        if (i < (n - 1)) {
          casingout = apsegshs[i + 1];
        } else {
          casingout = apsegshs[0];
        }
        sbond1(spinsh, casingout);
      }
      // Bond all subfaces share at p->b together.
      for (i = 0; i < n; i++) {
        spinsh = pbsegshs[i];
        if (i < (n - 1)) {
          casingout = pbsegshs[i + 1];
        } else {
          casingout = pbsegshs[0];
        }
        sbond1(spinsh, casingout);
      }
      delete [] apsegshs;
      delete [] pbsegshs;

      // Check for newly encroached subsegments if the flag is set.
      if (chkencseg) {
        // Check if a->p and p->b are encroached by other vertices.
        checkseg4encroach(&apseg, NULL, NULL, true);
        checkseg4encroach(&pbseg, NULL, NULL, true);
        // Check if the adjacent segments are encroached by p.
        tallencsegs(bp, n, ceillists);
      }
    } // if (splitseg != (face *) NULL) 

    // Delete subfaces of old CBC_i(p)s.
    for (k = 0; k < n; k++) {
      for (i = 0; i < sublists[k]->len(); i++) {
        oldsh = * (face *)(* (sublists[k]))[i];
        shellfacedealloc(subfaces, oldsh.sh);
      }
      // Clear the list so that the subs will not get unmarked later in
      //   routine releasebowatcavity() which only frees the memory.
      sublists[k]->clear();
      // Only do once if p is on a facet.
      if (splitseg == (face *) NULL) break; 
    }

    // Check for newly encroached subfaces if the flag is set.
    if (chkencsub) {
      // Check if new subfaces of C_i(p) are encroached by other vertices.
      for (k = 0; k < n; k++) {
        subceillist = subceillists[k];
        for (i = 0; i < subceillist->len(); i++) {
          newsh = * (face *)(* subceillist)[i];
          checksub4encroach(&newsh, NULL, true);
        }
        // Only do once if p is on a facet.
        if (splitseg == (face *) NULL) break; 
      }
      // Check if the adjacent subfaces are encroached by p.
      tallencsubs(bp, n, ceillists);
    }
  } // if (subceillists != (list **) NULL)

  // Delete tets of old BC_i(p)s.
  for (k = 0; k < n; k++) {
    for (i = 0; i < tetlists[k]->len(); i++) {
      oldtet = * (triface *)(* (tetlists[k]))[i];
      tetrahedrondealloc(oldtet.tet);
    }
    // Clear the list so that the tets will not get unmarked later in
    //   routine releasebowatcavity() which only frees the memory.
    tetlists[k]->clear();
  }

  // check for bad quality tets if the flags is set.
  if (chkbadtet) {
    for (k = 0; k < n; k++) {
      ceillist = ceillists[k];
      for (i = 0; i < ceillist->len(); i++) {
        newtet = * (triface *)(* ceillist)[i];
        checktet4badqual(&newtet, true);
      }
    }
  }

  if (flipque != (queue *) NULL) {
    // Newly created internal faces of BC(p) (excluding faces on C(p)s) are
    //   in 'flipque'.  Some of these faces may be locally non-Delaunay due
    //   to the existence of non-constrained tets. check and fix them.
    lawson3d(flipque);
  }
}

////                                                                       ////
////                                                                       ////
//// flip_cxx /////////////////////////////////////////////////////////////////

//// delaunay_cxx /////////////////////////////////////////////////////////////
////                                                                       ////
////                                                                       ////

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// btree_sort()    Sort vertices using a binary space partition (bsp) tree.  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::btree_sort(point* vertexarray, int arraysize, int axis, 
  REAL bxmin, REAL bxmax, REAL bymin, REAL bymax, REAL bzmin, REAL bzmax, 
  int depth)
{
  point *leftarray, *rightarray;
  point **pptary, swapvert;
  REAL split;
  bool lflag, rflag;
  int i, j, k;

  if (b->verbose > 2) {
    //printf("  Depth %d, %d verts. Bbox (%g, %g, %g),(%g, %g, %g). %s-axis\n",
           //depth, arraysize, bxmin, bymin, bzmin, bxmax, bymax, bzmax,
          // axis == 0 ? "x" : (axis == 1 ? "y" : "z"));
  }

  if (depth > max_btree_depth) {
    max_btree_depth = depth;
  }

  if (axis == 0) {
    // Split along x-axis.
    split = 0.5 * (bxmin + bxmax);
  } else if (axis == 1) {
    // Split along y-axis.
    split = 0.5 * (bymin + bymax);
  } else {
    // Split along z-axis.
    split = 0.5 * (bzmin + bzmax);
  }

  i = 0;
  j = arraysize - 1;

  // Partition the vertices into left- and right-arraies.
  do {
    for (; i < arraysize; i++) {
      if (vertexarray[i][axis] >= split) {
        break;
      }
    }
    for (; j >= 0; j--) {
      if (vertexarray[j][axis] < split) {
        break;
      }
    }
    // Is the partition finished?
    if (i == (j + 1)) {
      break;
    }
    // Swap i-th and j-th vertices.
    swapvert = vertexarray[i];
    vertexarray[i] = vertexarray[j];
    vertexarray[j] = swapvert;
    // Continue patitioning the array;
  } while (true);

  if (b->verbose > 2) {
    //printf("    leftsize = %d, rightsize = %d\n", i, arraysize - i);
  }
  lflag = rflag = false;

  // if (depth < max_tree_depth) {
    if (i > b->max_btreenode_size) {
      // Recursively partition the left array (length = i).
      if (axis == 0) { // x
        btree_sort(vertexarray, i, (axis + 1) % 3, bxmin, split, bymin, 
                   bymax, bzmin, bzmax, depth + 1);
      } else if (axis == 1) { // y
        btree_sort(vertexarray, i, (axis + 1) % 3, bxmin, bxmax, bymin, 
                   split, bzmin, bzmax, depth + 1);
      } else { // z
        btree_sort(vertexarray, i, (axis + 1) % 3, bxmin, bxmax, bymin, 
                   bymax, bzmin, split, depth + 1);
      }
    } else {
      lflag = true;
    }
    if ((arraysize - i) > b->max_btreenode_size) {
      // Recursively partition the right array (length = arraysize - i).
      if (axis == 0) { // x
        btree_sort(&(vertexarray[i]), arraysize - i, (axis + 1) % 3, split, 
                   bxmax, bymin, bymax, bzmin, bzmax, depth + 1);
      } else if (axis == 1) { // y
        btree_sort(&(vertexarray[i]), arraysize - i, (axis + 1) % 3, bxmin, 
                   bxmax, split, bymax, bzmin, bzmax, depth + 1);
      } else { // z
        btree_sort(&(vertexarray[i]), arraysize - i, (axis + 1) % 3, bxmin, 
                   bxmax, bymin, bymax, split, bzmax, depth + 1);
      }
    } else {
      rflag = true;
    }
  // } else {
  //   // Both left and right are done.
  //   lflag = rflag = true;
  // }

  if (lflag && (i > 0)) {
    // Remember the maximal length of the partitions.
    if (i > max_btreenode_size) {
      max_btreenode_size = i;
    }
    // Allocate space for the left array (use the first entry to save
    //   the length of this array).
    leftarray = new point[i + 1]; 
    leftarray[0] = (point) i; // The array lenth.
    // Put all points in this array.
    for (k = 0; k < i; k++) {
      leftarray[k + 1] = vertexarray[k];
      setpoint2ppt(leftarray[k + 1], (point) leftarray);
    }
    // Save this array in list.
    btreenode_list->newindex((void **) &pptary);
    *pptary = leftarray;
  }

  // Get the length of the right array.
  j = arraysize - i;
  if (rflag && (j > 0)) {
    if (j > max_btreenode_size) {
      max_btreenode_size = j;
    }
    // Allocate space for the right array (use the first entry to save
    //   the length of this array).
    rightarray = new point[j + 1];
    rightarray[0] = (point) j; // The array lenth.
    // Put all points in this array.
    for (k = 0; k < j; k++) {
      rightarray[k + 1] = vertexarray[i + k];
      setpoint2ppt(rightarray[k + 1], (point) rightarray);
    }
    // Save this array in list.
    btreenode_list->newindex((void **) &pptary);
    *pptary = rightarray;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// btree_insert()    Add a vertex into a tree node.                          //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::btree_insert(point insertpt)
{
  point *ptary;
  long arylen; // The array lenhgth is saved in ptary[0].

  // Get the tree node (save in this point).
  ptary = (point *) point2ppt(insertpt);
  // Get the current array length.
  arylen = (long) ptary[0];
  // Insert the point into the node.
  ptary[arylen + 1] = insertpt;
  // Increase the array length by 1.
  ptary[0] = (point) (arylen + 1);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// btree_search()    Search a near point for an inserting point.             //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::btree_search(point insertpt, triface* searchtet)
{
  point *ptary;
  point nearpt, candpt;
  REAL dist2, mindist2;
  int ptsamples, ptidx;
  long arylen;
  int i;

  // Get the tree node (save in this point).
  ptary = (point *) point2ppt(insertpt);
  // Get the current array length.
  arylen = (long) ptary[0];

  if (arylen == 0) {
    searchtet->tet = NULL;
    return;
  }

  if (arylen < 10) {
    ptsamples = arylen;
  } else {
    ptsamples = 10; // Take at least 10 samples.
    //   The number of random samples taken is proportional to the third root
    //   of the number of points in the cell.
    while (ptsamples * ptsamples * ptsamples < arylen) {
      ptsamples++;
    }
  }

  // Select "good" candidate using k random samples, taking the closest one.
  mindist2 = 1.79769E+308; // The largest double value (8 byte).
  nearpt = NULL;

  for (i = 0; i < ptsamples; i++) {
    ptidx = randomnation((unsigned long) arylen);
    candpt = ptary[ptidx + 1];
    dist2 = (candpt[0] - insertpt[0]) * (candpt[0] - insertpt[0])
          + (candpt[1] - insertpt[1]) * (candpt[1] - insertpt[1])
          + (candpt[2] - insertpt[2]) * (candpt[2] - insertpt[2]);
    if (dist2 < mindist2) {
      mindist2 = dist2;
      nearpt = candpt;
    }
  }

  if (b->verbose > 1) {
    //printf("    Get point %d (cell size %ld).\n", pointmark(nearpt), arylen);
  }

  decode(point2tet(nearpt), *searchtet);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// ordervertices()    Order the vertices for incremental inserting.          //
//                                                                           //
// We assume the vertices have been sorted by a binary tree.                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::ordervertices(point* vertexarray, int arraysize)
{
  point **ipptary, **jpptary, *swappptary; 
  point *ptary;
  long arylen;
  int index, i, j;

  // First pick one vertex from each tree node.
  for (i = 0; i < (int) btreenode_list->objects; i++) {
    ipptary = (point **) fastlookup(btreenode_list, i);
    ptary = *ipptary;
    vertexarray[i] = ptary[1]; // Skip the first entry.
  }

  index = i;
  // Then put all other points in the array node by node.
  for (i = (int) btreenode_list->objects - 1; i >= 0; i--) {
    // Randomly pick a tree node.
    j = randomnation(i + 1);
    // Save the i-th node.
    ipptary = (point **) fastlookup(btreenode_list, i);
    // Get the j-th node.
    jpptary = (point **) fastlookup(btreenode_list, j);
    // Order the points in the node.
    ptary = *jpptary;
    arylen = (long) ptary[0];
    for (j = 2; j <= arylen; j++) { // Skip the first point.
      vertexarray[index] = ptary[j];
      index++;
    }
    // Clear this tree node.
    ptary[0] = (point) 0;
    // Swap i-th node to j-th node.
    swappptary = *ipptary;
    *ipptary = *jpptary; // [i] <= [j]
    *jpptary = swappptary; // [j] <= [i]
  }

  // Make sure we've done correctly.
  //assert(index == arraysize);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// insertvertexbw()    Insert a vertex using the Boywer-Watson algorithm.    //
//                                                                           //
// The point p will be first located in T. 'searchtet' is a suggested start- //
// tetrahedron, it can be NULL. Note that p may lies outside T. In such case,//
// the convex hull of T will be updated to include p as a vertex.            //
//                                                                           //
// If 'bwflag' is TRUE, the Bowyer-Watson algorithm is used to recover the   //
// Delaunayness of T. Otherwise, do nothing with regard to the Delaunayness  //
// T (T may be non-Delaunay after this function).                            //
//                                                                           //
// If 'visflag' is TRUE, force to check the visibility of the boundary faces //
// of cavity. This is needed when T is not Delaunay.                         //
//                                                                           //
// If 'noencflag' is TRUE, only insert the new point p if it does not cause  //
// any existing (sub)segment be non-Delaunay. This option only is checked    //
// when the global variable 'checksubsegs' is set.                           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

enum tetgenmesh::locateresult tetgenmesh::insertvertexbw(point insertpt, 
  triface *searchtet, bool bwflag, bool visflag, bool noencsegflag,
  bool noencsubflag)
{
  triface neightet, spintet, newtet, neineitet;
  triface *cavetet, *parytet, *parytet1;
  face checksh, *pssub;
  face checkseg, *paryseg;
  point pa, pb, pc, *ppt;
  enum locateresult loc;
  REAL attrib, volume;
  REAL sign, ori;
  long tetcount;
  bool enqflag;
  int hitbdry;
  int i, j;

  arraypool *swaplist; // for updating cavity.
  long updatecount;

 /* if (b->verbose > 1) {
    //printf("    Insert point %d\n", pointmark(insertpt));
  }*/

  tetcount = ptloc_count;
  updatecount = 0; 

  // Locate the point.
  if (searchtet->tet == NULL) {
    if (btreenode_list) { // default option
      // Use bsp-tree to select a starting tetrahedron.
      btree_search(insertpt, searchtet);
    } else { // -u0 option
      // Randomly select a starting tetrahedron.
      randomsample(insertpt, searchtet);
    }
    loc = preciselocate(insertpt, searchtet, tetrahedrons->items);
  } else {
    // Start from 'searchtet'.
    loc = locate2(insertpt, searchtet, NULL);
  }

  /*if (b->verbose > 1) {
    //printf("    Walk distance (# tets): %ld\n", ptloc_count - tetcount);
  }*/

  if (ptloc_max_count < (ptloc_count - tetcount)) {
    ptloc_max_count = (ptloc_count - tetcount);
  }

  if (b->verbose > 1) {
    ////printf("    Located (%d) tet (%d, %d, %d, %d).\n", (int) loc,
      //pointmark(org(*searchtet)), pointmark(dest(*searchtet)), 
     // pointmark(apex(*searchtet)), pointmark(oppo(*searchtet)));
  }

  if (loc == ONVERTEX) {
    // The point already exists. Mark it and do nothing on it.
    if (b->object != tetgenbehavior::STL) {
      if (!b->quiet) {
        ////printf("Warning:  Point #%d is duplicated with Point #%d. Ignored!\n",
          //pointmark(insertpt), pointmark(org(*searchtet)));
      }
    }
    setpoint2ppt(insertpt, org(*searchtet));
    setpointtype(insertpt, DUPLICATEDVERTEX);
    dupverts++;
    return loc;
  }

  tetcount = 0l;  // The number of deallocated tets.

  // Create the initial boundary of the cavity.
  if (loc == INTETRAHEDRON) {
    // Add four boundary faces of this tet into list.
    neightet.tet = searchtet->tet;
    for (neightet.loc = 0; neightet.loc < 4; neightet.loc++) {
      cavetetlist->newindex((void **) &parytet);
      *parytet = neightet;
    }
    infect(*searchtet);
    caveoldtetlist->newindex((void **) &parytet);
    *parytet = *searchtet;
    tetcount++;
    flip14count++;
  } else if (loc == ONFACE) {
    // Add at most six boundary faces into list.
    neightet.tet = searchtet->tet;
    for (i = 0; i < 3; i++) {
      neightet.loc = locpivot[searchtet->loc][i];
      cavetetlist->newindex((void **) &parytet);
      *parytet = neightet;
    }
    infect(*searchtet);
    caveoldtetlist->newindex((void **) &parytet);
    *parytet = *searchtet;
    tetcount++;
    decode(searchtet->tet[searchtet->loc], spintet);
    if (spintet.tet != dummytet) {
      neightet.tet = spintet.tet;
      for (i = 0; i < 3; i++) {
        neightet.loc = locpivot[spintet.loc][i];
        cavetetlist->newindex((void **) &parytet);
        *parytet = neightet;
      }
      infect(spintet);
      caveoldtetlist->newindex((void **) &parytet);
      *parytet = spintet;
      tetcount++;
    } else {
      // Split a hull face into three hull faces.
      hullsize += 2;
    }
    flip26count++;
  } else if (loc == ONEDGE) {
    // Add all adjacent boundary tets into list.
    spintet = *searchtet;
    pc = apex(spintet);
    hitbdry = 0;
    do {
      tetcount++;
      neightet.tet = spintet.tet;
      neightet.loc = locverpivot[spintet.loc][spintet.ver][0];
      cavetetlist->newindex((void **) &parytet);
      *parytet = neightet;
      neightet.loc = locverpivot[spintet.loc][spintet.ver][1];
      cavetetlist->newindex((void **) &parytet);
      *parytet = neightet;
      infect(spintet);
      caveoldtetlist->newindex((void **) &parytet);
      *parytet = spintet;
      // Go to the next tet (may be dummytet).
      tfnext(spintet, neightet);
      if (neightet.tet == dummytet) {
        hitbdry++;
        if (hitbdry == 2) break;
        esym(*searchtet, spintet); // Go to another direction.
        tfnext(spintet, neightet);
        if (neightet.tet == dummytet) break;
      }
      spintet = neightet;
    } while (apex(spintet) != pc);
    // Update hull size if it is a hull edge.
    if (hitbdry > 0) {
      // Split a hull edge deletes two hull faces, adds four new hull faces.
      hullsize += 2;
    }
    flipn2ncount++;
  } else if (loc == OUTSIDE) {
    // p lies outside the convex hull. Enlarge the convex hull by including p.
    if (b->verbose > 1) {
      ////printf("    Insert a hull vertex.\n");
    }
    // 'searchtet' refers to a hull face which is visible by p. 
    adjustedgering(*searchtet, CW);
    // Create the first tet t (from f and p).
    maketetrahedron(&newtet);
    setorg (newtet, org(*searchtet));
    setdest(newtet, dest(*searchtet));
    setapex(newtet, apex(*searchtet));
    setoppo(newtet, insertpt);
    for (i = 0; i < in->numberoftetrahedronattributes; i++) {
      attrib = elemattribute(searchtet->tet, i);
      setelemattribute(newtet.tet, i, attrib);
    }
    if (b->varvolume) {
      volume = volumebound(searchtet->tet);
      setvolumebound(newtet.tet, volume);
    }
    // Connect t to T.
    bond(newtet, *searchtet);
    // Removed a hull face, added three "new hull faces".
    hullsize += 2;

    // Add a cavity boundary face.
    cavetetlist->newindex((void **) &parytet);
    *parytet = newtet;
    // Add a cavity tet.
    infect(newtet);
    caveoldtetlist->newindex((void **) &parytet);
    *parytet = newtet;
    tetcount++;

    // Add three "new hull faces" into list (re-use cavebdrylist).
    newtet.ver = 0;
    for (i = 0; i < 3; i++) {
      fnext(newtet, neightet);
      cavebdrylist->newindex((void **) &parytet);
      *parytet = neightet;
      enextself(newtet);
    }

    // Find all actual new hull faces.
    for (i = 0; i < (int) cavebdrylist->objects; i++) {
      // Get a queued "new hull face".
      parytet = (triface *) fastlookup(cavebdrylist, i);
      // Every "new hull face" must have p as its apex.
      //assert(apex(*parytet) == insertpt);
      //assert((parytet->ver & 1) == 1); // It's CW edge ring.
      // Check if it is still a hull face.
      sym(*parytet, neightet);
      if (neightet.tet == dummytet) {
        // Yes, get its adjacent hull face (at its edge).
        esym(*parytet, neightet);
        while (1) {
          fnextself(neightet);
          // Does its adjacent tet exist?
          sym(neightet, neineitet);
          if (neineitet.tet == dummytet) break;
          symedgeself(neightet);
        }
        // neightet is an adjacent hull face.
        pc = apex(neightet);
        if (pc != insertpt) {
          // Check if p is visible by the hull face ('neightet').
          pa = org(neightet);
          pb = dest(neightet);
          ori = orient3d(pa, pb, pc, insertpt); orient3dcount++;
          if (ori < 0) {
            // Create a new tet adjacent to neightet.
            maketetrahedron(&newtet);
            setorg (newtet, pa);
            setdest(newtet, pb);
            setapex(newtet, pc);
            setoppo(newtet, insertpt);
            for (j = 0; j < in->numberoftetrahedronattributes; j++) {
              attrib = elemattribute(neightet.tet, j);
              setelemattribute(newtet.tet, j, attrib);
            }
            if (b->varvolume) {
              volume = volumebound(neightet.tet);
              setvolumebound(newtet.tet, volume);
            }
            bond(newtet, neightet);
            fnext(newtet, neineitet);
            bond(neineitet, *parytet);
            // Comment: We removed two hull faces, and added two "new hull
            //   faces", hence hullsize remains unchanged.
            // Add a cavity boundary face.
            cavetetlist->newindex((void **) &parytet1);
            *parytet1 = newtet;
            // Add a cavity tet.
            infect(newtet);
            caveoldtetlist->newindex((void **) &parytet1);
            *parytet1 = newtet;
            tetcount++;
            // Add two "new hull faces" into list.
            enextself(newtet);
            for (j = 0; j < 2; j++) {
              fnext(newtet, neineitet);
              cavebdrylist->newindex((void **) &parytet1);
              *parytet1 = neineitet;
              enextself(newtet);
            }
          }
        } else {
          // Two hull faces matched. Bond the two adjacent tets.
          bond(*parytet, neightet);
          hullsize -= 2;
        }
      } // if (neightet.tet == dummytet)
    } // i
    cavebdrylist->restart();
    inserthullcount++;
  }

  if (!bwflag) return loc;

  // Form the Boywer-Watson cavity.
  for (i = 0; i < (int) cavetetlist->objects; i++) {
    // Get a cavity boundary face.
    parytet = (triface *) fastlookup(cavetetlist, i);
    //assert(parytet->tet != dummytet);
    //assert(infected(*parytet)); // The tet is inside the cavity.
    enqflag = false;
    // Get the adjacent tet.
    sym(*parytet, neightet);
    if (neightet.tet != dummytet) {
      if (!infected(neightet)) {
        if (!marktested(neightet)) {
          ppt = (point *) &(neightet.tet[4]);
          sign = insphere_s(ppt[0], ppt[1], ppt[2], ppt[3], insertpt);
          enqflag = (sign < 0.0);
          // Avoid redundant insphere tests.
          marktest(neightet);
        }
      } else {
        enqflag = true;
      }
    }
    if (enqflag) { // Found a tet in the cavity.
      if (!infected(neightet)) { // Avoid to add it multiple times.
        // Put other three faces in check list.
        neineitet.tet = neightet.tet;
        for (j = 0; j < 3; j++) {
          neineitet.loc = locpivot[neightet.loc][j];
          cavetetlist->newindex((void **) &parytet1);
          *parytet1 = neineitet;
        }
        infect(neightet);
        caveoldtetlist->newindex((void **) &parytet1);
        *parytet1 = neightet;
        tetcount++;
      }
    } else {
      // Found a boundary face of the cavity.
      if (neightet.tet == dummytet) {
        // Check for a possible flat tet (see m27.node, use -J option).
        pa = org(*parytet);
        pb = dest(*parytet);
        pc = apex(*parytet);
        ori = orient3d(pa, pb, pc, insertpt);
        if (ori != 0) {
          cavebdrylist->newindex((void **) &parytet1);
          *parytet1 = *parytet;
          // futureflip = flippush(futureflip, parytet, insertpt);
        }
      } else {
        cavebdrylist->newindex((void **) &parytet1);
        *parytet1 = *parytet;
      }
    }
  } // i

  if (b->verbose > 1) {
   // //printf("    Cavity formed: %ld tets, %ld faces.\n", tetcount,
         //  cavebdrylist->objects);
  }

  totaldeadtets += tetcount;
  totalbowatcavsize += cavebdrylist->objects;
  if (maxbowatcavsize < (long) cavebdrylist->objects) {
    maxbowatcavsize = cavebdrylist->objects;
  }

  if (checksubsegs || noencsegflag) {
    // Check if some (sub)segments are inside the cavity.
    for (i = 0; i < (int) caveoldtetlist->objects; i++) {
      parytet = (triface *) fastlookup(caveoldtetlist, i);
      for (j = 0; j < 6; j++) {
        parytet->loc = edge2locver[j][0];
        parytet->ver = edge2locver[j][1];
        tsspivot1(*parytet, checkseg);
        if ((checkseg.sh != dummysh) && !sinfected(checkseg)) {
          // Check if this segment is inside the cavity.
          spintet = *parytet;
          pa = apex(spintet);
          enqflag = true;
          hitbdry = 0;
          while (1) {
            tfnextself(spintet);
            if (spintet.tet == dummytet) {
              hitbdry++;
              if (hitbdry == 2) break;
              esym(*parytet, spintet);
              tfnextself(spintet);
              if (spintet.tet == dummytet) break;
            }
            if (!infected(spintet)) {
              enqflag = false; break; // It is not inside.
            }
            if (apex(spintet) == pa) break;
          }
          if (enqflag) {
            if (b->verbose > 1) {
             // //printf("      Queue a missing segment (%d, %d).\n",
             //   pointmark(sorg(checkseg)), pointmark(sdest(checkseg)));
            }
            sinfect(checkseg);  // Only save it once.
            subsegstack->newindex((void **) &paryseg);
            *paryseg = checkseg;
          }
        }
      }
    }
  }

  if (noencsegflag && (subsegstack->objects > 0)) {
    // Found encroached subsegments! Do not insert this point.
    for (i = 0; i < (int) caveoldtetlist->objects; i++) {
      parytet = (triface *) fastlookup(caveoldtetlist, i);
      uninfect(*parytet);
      unmarktest(*parytet);
    }
    // Unmark cavity neighbor tets (outside the cavity).
    for (i = 0; i < (int) cavebdrylist->objects; i++) {
      parytet = (triface *) fastlookup(cavebdrylist, i);
      sym(*parytet, neightet);
      if (neightet.tet != dummytet) {
        unmarktest(neightet);
      }
    }
    cavetetlist->restart();
    cavebdrylist->restart();
    caveoldtetlist->restart();
    return ENCSEGMENT;
  }

  if (checksubfaces || noencsubflag) {
    // Check if some subfaces are inside the cavity.
    for (i = 0; i < (int) caveoldtetlist->objects; i++) {
      parytet = (triface *) fastlookup(caveoldtetlist, i);
      neightet.tet = parytet->tet;
      for (neightet.loc = 0; neightet.loc < 4; neightet.loc++) {
        tspivot(neightet, checksh);
        if (checksh.sh != dummysh) {
          sym(neightet, neineitet);
          // Do not check it if it is a hull tet.
          if (neineitet.tet != dummytet) {
            if (infected(neineitet)) {
              if (b->verbose > 1) {
                //printf("      Queue a missing subface (%d, %d, %d).\n",
                  //pointmark(sorg(checksh)), pointmark(sdest(checksh)),
                 // pointmark(sapex(checksh)));
              }
              tsdissolve(neineitet); // Disconnect a tet-sub bond.
              stdissolve(checksh); // Disconnect the sub-tet bond.
              sesymself(checksh);
              stdissolve(checksh);
              // Add the missing subface into list.
              subfacstack->newindex((void **) &pssub);
              *pssub = checksh;
            }
          }
        }
      }
    }
  }

  if (noencsubflag && (subfacstack->objects > 0)) {
    // Found encroached subfaces! Do not insert this point.
    /*for (i = 0; i < caveoldtetlist->objects; i++) {
      cavetet = (triface *) fastlookup(caveoldtetlist, i);
      uninfect(*cavetet);
      unmarktest(*cavetet);
    }
    for (i = 0; i < cavebdrylist->objects; i++) {
      cavetet = (triface *) fastlookup(cavebdrylist, i);
      unmarktest(*cavetet); // Unmark it.
    }
    if (bwflag && (futureflip != NULL)) {
      flippool->restart();
      futureflip = NULL;
    }
    cavetetlist->restart();
    cavebdrylist->restart();
    caveoldtetlist->restart();
    return ENCFACE;
    */
  }

  if (visflag) {
    // If T is not a Delaunay triangulation, the formed cavity may not be
    //   star-shaped (fig/dump-cavity-case8). Validation is needed.
    cavetetlist->restart(); // Re-use it.
    for (i = 0; i < (int) cavebdrylist->objects; i++) {
      cavetet = (triface *) fastlookup(cavebdrylist, i);
      if (infected(*cavetet)) {
        sym(*cavetet, neightet);
        if (neightet.tet == dummytet || !infected(neightet)) {
          if (neightet.tet != dummytet) {
            cavetet->ver = 4; // CCW edge ring.
            pa = dest(*cavetet);
            pb = org(*cavetet);
            pc = apex(*cavetet);
            ori = orient3d(pa, pb, pc, insertpt); orient3dcount++;
            //assert(ori != 0.0); // SELF_CHECK
            enqflag = (ori > 0.0);
          } else {
            enqflag = true; // A hull face.
          }
          if (enqflag) {
            // This face is valid, save it.
            cavetetlist->newindex((void **) &parytet);
            *parytet = *cavetet; 
          } else {
            if (b->verbose > 1) {
              //printf("    Cut tet (%d, %d, %d, %d)\n", pointmark(pb), 
                //pointmark(pa), pointmark(pc), pointmark(oppo(*cavetet)));
            }
            uninfect(*cavetet);
            unmarktest(*cavetet);
            if (neightet.tet != dummytet) {
              unmarktest(neightet);
            }
            updatecount++;
            // Add three new faces to find new boundaries.
            for (j = 0; j < 3; j++) {
              fnext(*cavetet, neineitet);
              sym(neineitet, neightet);
              if (neightet.tet != dummytet) {
                if (infected(neightet)) { 
                  neightet.ver = 4;
                  cavebdrylist->newindex((void **) &parytet);
                  *parytet = neightet;
                } else {
                  unmarktest(neightet);
                }
              }
              enextself(*cavetet);
            }
          }
        } else {
          // This face is not on the cavity boundary anymore.
          unmarktest(*cavetet);
        }
      } else {
        //assert(!marktested(*cavetet));
      }
    }
    if (updatecount > 0) {
      // Update the cavity boundary faces (fig/dump-cavity-case9).
      cavebdrylist->restart();
      for (i = 0; i < (int) cavetetlist->objects; i++) {
        cavetet = (triface *) fastlookup(cavetetlist, i);
        // 'cavetet' was boundary face of the cavity.
        if (infected(*cavetet)) {
          sym(*cavetet, neightet);
          if ((neightet.tet != dummytet) || !infected(neightet)) {
            // It is a cavity boundary face.
            cavebdrylist->newindex((void **) &parytet);
            *parytet = *cavetet;
          } else {
            // Not a cavity boundary face.
            unmarktest(*cavetet);
          }
        } else {
          //assert(!marktested(*cavetet));
        }
      }
      // Update the list of old tets.
      cavetetlist->restart();
      for (i = 0; i < (int) caveoldtetlist->objects; i++) {
        cavetet = (triface *) fastlookup(caveoldtetlist, i);
        if (infected(*cavetet)) {
          cavetetlist->newindex((void **) &parytet);
          *parytet = *cavetet;
        }
      }
      //assert((int) cavetetlist->objects < i);
      // Swap 'cavetetlist' and 'caveoldtetlist'.
      swaplist = caveoldtetlist;
      caveoldtetlist = cavetetlist;
      cavetetlist = swaplist;
      if (b->verbose > 1) {
        //printf("    Size of the updated cavity: %d faces %d tets.\n",
          //(int) cavebdrylist->objects, (int) caveoldtetlist->objects);
      }
    }
  }

  // Re-use this list for new cavity faces.
  cavetetlist->restart();

  // Create new tetrahedra in the Bowyer-Watson cavity and Connect them.
  for (i = 0; i < (int) cavebdrylist->objects; i++) {
    parytet = (triface *) fastlookup(cavebdrylist, i);
    //assert(infected(*parytet)); // The tet is inside the cavity.
    parytet->ver = 0; // In CCW edge ring.
    maketetrahedron(&newtet);
    setorg (newtet, org(*parytet));
    setdest(newtet, dest(*parytet));
    setapex(newtet, apex(*parytet));
    setoppo(newtet, insertpt);
    for (j = 0; j < in->numberoftetrahedronattributes; j++) {
      attrib = elemattribute(parytet->tet, j);
      setelemattribute(newtet.tet, j, attrib);
    }
    if (b->varvolume) {
      volume = volumebound(parytet->tet);
      setvolumebound(newtet.tet, volume);
    }
    // Bond the new tet to the adjacent tet outside the cavity.
    sym(*parytet, neightet);
    if (neightet.tet != dummytet) {
      // The tet was marked (to avoid redundant insphere tests).
      unmarktest(neightet);
      bond(newtet, neightet);
    } else {
      // Bond newtet to dummytet.
      dummytet[0] = encode(newtet);
    }
    // mark the other three faces of this tet as "open".
    neightet.tet = newtet.tet;
    for (j = 0; j < 3; j++) {
      neightet.tet[locpivot[0][j]] = NULL;
    }
    // Let the oldtet knows newtet (for connecting adjacent new tets).
    parytet->tet[parytet->loc] = encode(newtet);
    if (checksubsegs) {
      // newtet and parytet share at the same edge. 
      for (j = 0; j < 3; j++) {
        tsspivot1(*parytet, checkseg);
        if (checkseg.sh != dummysh) {
          if (sinfected(checkseg)) {
            // This subsegment is not missing. Unmark it.
            if (b->verbose > 1) {
              //printf("      Dequeue a segment (%d, %d).\n",
                //pointmark(sorg(checkseg)), pointmark(sdest(checkseg)));
            }
            suninfect(checkseg); // Dequeue a non-missing segment.
          }
          tssbond1(newtet, checkseg);
        }
        enextself(*parytet);
        enextself(newtet);
      }
    }
    if (checksubfaces) {
      // Bond subface to the new tet.
      tspivot(*parytet, checksh);
      if (checksh.sh != dummysh) {
        tsbond(newtet, checksh);
        // The other-side-connection of checksh should be no change.
      }
    }
  } // i

  // Set a handle for speeding point location.
  recenttet = newtet;
  setpoint2tet(insertpt, encode(newtet));

  // Connect adjacent new tetrahedra together. Here we utilize the connections
  //   of the old cavity tets to find the new adjacent tets.
  for (i = 0; i < (int) cavebdrylist->objects; i++) {
    parytet = (triface *) fastlookup(cavebdrylist, i);
    decode(parytet->tet[parytet->loc], newtet);
    // //assert(org(newtet) == org(*parytet)); // SELF_CHECK
    // //assert((newtet.ver & 1) == 0); // in CCW edge ring.
    for (j = 0; j < 3; j++) {
      fnext(newtet, neightet); // Go to the "open" face.
      if (neightet.tet[neightet.loc] == NULL) {
        spintet = *parytet;
        while (1) {
          fnextself(spintet);
          symedgeself(spintet);
          if (spintet.tet == dummytet) break;
          if (!infected(spintet)) break;
        }
        if (spintet.tet != dummytet) {
          // 'spintet' is the adjacent tet of the cavity.
          fnext(spintet, neineitet);
          //assert(neineitet.tet[neineitet.loc] == NULL); // SELF_CHECK
          bond(neightet, neineitet);
        } else {
          // This side is a hull face.
          neightet.tet[neightet.loc] = (tetrahedron) dummytet;
          dummytet[0] = encode(neightet);
        }
      }
      setpoint2tet(org(newtet), encode(newtet));
      enextself(newtet);
      enextself(*parytet);
    }
  }

  // Delete the old cavity tets.
  for (i = 0; i < (int) caveoldtetlist->objects; i++) {
    parytet = (triface *) fastlookup(caveoldtetlist, i);
    tetrahedrondealloc(parytet->tet);
  }

  // Set the point type.
  if (pointtype(insertpt) == UNUSEDVERTEX) {
    setpointtype(insertpt, FREEVOLVERTEX);
  }

  if (btreenode_list) {
    btree_insert(insertpt);
  }

  cavetetlist->restart();
  cavebdrylist->restart();
  caveoldtetlist->restart();

  return loc;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// unifypoint()    Unify two distinct points if they're very close.          //
//                                                                           //
// This function is used for dealing with inputs from CAD tools.  Two points //
// p and q are unified if: dist(p, q) / longest < eps.  Where dist() is the  //
// Euclidean distance between p and q, longest is the maximum edge size of   //
// the input point set, eps is the tolerrence specified by user, default is  //
// 1e-6, it can be adjusted by '-T' switch.                                  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::unifypoint(point testpt, triface *starttet, enum locateresult
  loc, REAL eps)
{
  triface symtet, spintet;
  point checkpt, tapex;
  REAL tol;
  bool merged;
  int hitbdry;
  int i;

  merged = false;
  tol = longest * eps;
  if ((loc == OUTSIDE) || (loc == INTETRAHEDRON) || (loc == ONFACE)) {
    // Check p is close to the four corners of the tet.
    for (i = 0; i < 4; i++) {
      checkpt = (point) starttet->tet[4 + i];
      if (distance(testpt, checkpt) < tol) {
        merged = true; // Found a merge point p'.
        break;
      }
    }
    if (!merged && (loc == ONFACE)) {
      // Check the opposite point of the neighbor tet if it exists.
      sym(*starttet, symtet);
      if (symtet.tet != dummytet) {
        checkpt = oppo(symtet);
        if (distance(testpt, checkpt) < tol) {
          merged = true; // Found a merge point p'.
        }
      }
    }
  } else if (loc == ONEDGE) {
    // Check two endpoints of the edge.
    checkpt = org(*starttet);
    if (distance(testpt, checkpt) < tol) {
      merged = true; // Found a merge point p'.
    }
    if (!merged) {
      checkpt = dest(*starttet);
      if (distance(testpt, checkpt) < tol) {
        merged = true; // Found a merge point p'.
      }
    }
    if (!merged) {
      // Check apexes of the faces having the edge.
      spintet = *starttet;
      tapex = apex(*starttet);
      hitbdry = 0;
      do {
        checkpt = apex(spintet);
        if (distance(testpt, checkpt) < tol) {
          merged = true; // Found a merge point p'.
          break;
        }
        if (!fnextself(spintet)) {
          hitbdry++;
          if (hitbdry < 2) {
            esym(*starttet, spintet);
            if (!fnextself(spintet)) {
              hitbdry++;
            }
          }
        }
      } while ((apex(spintet) != tapex) && (hitbdry < 2));
    }
  }
  if (merged) {
    if (b->object != tetgenbehavior::STL) {
      if (!b->quiet) {
        //printf("Warning:  Point %d is unified to point %d.\n",
               //pointmark(testpt), pointmark(checkpt));
      }
      // Count the number of duplicated points.
      dupverts++;
    }
    // Remember it is a duplicated point.
    setpointtype(testpt, DUPLICATEDVERTEX);
    // Set a pointer to the point it duplicates.
    setpoint2ppt(testpt, checkpt);
  }
  return merged;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// incrflipdelaunay()   Construct a delaunay tetrahedrization from a set of  //
//                      3D points by the incremental flip algorithm.         //
//                                                                           //
// The incremental flip algorithm (by Edelsbrunner and Shah) can be describ- //
// ed as follows:                                                            //
//                                                                           //
//   S be a set of points in 3D, Let 4 <= i <= n and assume that the         //
//   Delaunay tetrahedralization of the first i-1 points in S is already     //
//   constructed; call it D(i-1). Add the i-th point p_i (belong to S) to    //
//   D(i-1), and restore Delaunayhood by flipping; this result in D(i).      //
//   Repeat this procedure until i = n.                                      //
//                                                                           //
// This strategy always leads to the Delaunay triangulation of a point set.  //
// The return value is the number of convex hull faces of D.                 //
//                                                                           //
// If the input point set is degenerate, i.e., all points are collinear or   //
// are coplanar, then no 3D DT is created and return FALSE.                  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::incrflipdelaunay(triface* oldtet, point* insertarray, 
  long arraysize, bool jump, bool merge, REAL eps, queue* flipque)
{
  triface newtet, searchtet;
  point swappt, lastpt;
  enum locateresult loc;
  REAL det;
  REAL attrib, volume;
  int i, j;

  // The initial tetrahedralization T only has one tet formed by 4 affinely
  //   linear independent vertices of the point set V = 'insertarray'. The
  //   first point a = insertarray[0].
 
  // Get the second point b, that is not identical or very close to a.
  for (i = 1; i < arraysize; i++) {
    det = distance(insertarray[0], insertarray[i]);
    if (det > (longest * eps)) break;
  }
  if (i == arraysize) {
    // //printf("\nAll points seem to be identical.\n");
    return false;
  } else {
    // Swap to move b from index i to index 1.
    swappt = insertarray[i];
    insertarray[i] = insertarray[1];
    insertarray[1] = swappt;  
  }
  // Get the third point c, that is not collinear with a and b.
  for (i++; i < arraysize; i++) {
    if (!iscollinear(insertarray[0], insertarray[1], insertarray[i], eps)) 
      break;
  }
  if (i == arraysize) {
    // //printf("\nAll points seem to be collinear.\n");
    return false;
  } else {
    // Swap to move c from index i to index 2.
    swappt = insertarray[i];
    insertarray[i] = insertarray[2];
    insertarray[2] = swappt;
  }
  // Get the fourth point d, that is not coplanar with a, b, and c.
  for (i++; i < arraysize; i++) {
    det = orient3d(insertarray[0], insertarray[1], insertarray[2],
                   insertarray[i]);
    if (det == 0.0) continue;
    if (!iscoplanar(insertarray[0], insertarray[1], insertarray[2],
                    insertarray[i], det, eps)) break;
  }
  if (i == arraysize) {
    return false;
  } else {
    // Swap to move d from index i to index 3.
    swappt = insertarray[i];
    insertarray[i] = insertarray[3];
    insertarray[3] = swappt;
    lastpt = insertarray[3];
    // The index of the next inserting point is 4.
    i = 4;
  }

  if (det > 0.0) {
    // For keeping the positive orientation.
    swappt = insertarray[0];
    insertarray[0] = insertarray[1];
    insertarray[1] = swappt;
  }

  // Create the initial tet.
  /*if (b->verbose > 1) {
    //printf("    Create the first tet (%d, %d, %d, %d).\n",
           pointmark(insertarray[0]), pointmark(insertarray[1]),
           pointmark(insertarray[2]), pointmark(lastpt));
  }*/

  maketetrahedron(&newtet);
  setorg(newtet, insertarray[0]);
  setdest(newtet, insertarray[1]);
  setapex(newtet, insertarray[2]);
  setoppo(newtet, lastpt);
  if (oldtet != (triface *) NULL) {
    for (j = 0; j < in->numberoftetrahedronattributes; j++) {
      attrib = elemattribute(oldtet->tet, j);
      setelemattribute(newtet.tet, j, attrib);
    }
    if (b->varvolume) {
      volume = volumebound(oldtet->tet);
      setvolumebound(newtet.tet, volume);
    }
  }
  // Set vertex type be FREEVOLVERTEX if it has no type yet.
  if (pointtype(insertarray[0]) == UNUSEDVERTEX) {
    setpointtype(insertarray[0], FREEVOLVERTEX);
  }
  if (pointtype(insertarray[1]) == UNUSEDVERTEX) {
    setpointtype(insertarray[1], FREEVOLVERTEX);
  }
  if (pointtype(insertarray[2]) == UNUSEDVERTEX) {
    setpointtype(insertarray[2], FREEVOLVERTEX);
  }
  if (pointtype(lastpt) == UNUSEDVERTEX) {
    setpointtype(lastpt, FREEVOLVERTEX);
  }
  // Bond to 'dummytet' for point location.
  dummytet[0] = encode(newtet);
  recenttet = newtet;
  // Update the point-to-tet map.
  setpoint2tet(insertarray[0], encode(newtet));
  setpoint2tet(insertarray[1], encode(newtet));
  setpoint2tet(insertarray[2], encode(newtet));
  setpoint2tet(lastpt, encode(newtet));
  /*if (b->verbose > 3) {
    //printf("    Creating tetra ");
    printtet(&newtet);
  }*/
  // At init, all faces of this tet are hull faces.
  hullsize = 4;

  /*if (b->verbose > 1) {
    //printf("    Incrementally inserting points.\n");
  }*/

  // Insert the rest of points, one by one.
  for (; i < arraysize; i++) {
    if (jump) {
      searchtet.tet = NULL;
    } else {
      searchtet = recenttet;
    }
    loc = insertvertexbw(insertarray[i],&searchtet,true,false,false,false);
  }

  return true;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// delaunizevertices()    Form a Delaunay tetrahedralization.                //
//                                                                           //
// Given a point set V (saved in 'points').  The Delaunay tetrahedralization //
// D of V is created by incrementally inserting vertices. Returns the number //
// of triangular faces bounding the convex hull of D.                        //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

__device__ long tetgenmesh::delaunizevertices()
{
  point *insertarray;
  long arraysize;
  bool success;
  int i, j;

  /*if (!b->quiet) {
    //printf("Constructing Delaunay tetrahedralization.\n");
  }*/

  //if (b->btree) {
  //  btreenode_list = new arraypool(sizeof(point*), 10);
  //  max_btreenode_size = 0;
  //  max_btree_depth = 0;
  //}

  if (cavetetlist == NULL) {
	  //marktag
    /*cavetetlist = new arraypool(sizeof(triface), 10);
    cavebdrylist = new arraypool(sizeof(triface), 10);
    caveoldtetlist = new arraypool(sizeof(triface), 10);*/
  }

  // Prepare the array of points for inserting.
  arraysize = points->items;
  //marktag
  //insertarray = new point[arraysize];

  points->traversalinit();
  //if (b->btree) { // -u option.
  //  // Use the input order.
  //  for (i = 0; i < arraysize; i++) {
  //    insertarray[i] = pointtraverse();
  //  }
  //  if (b->verbose) {
  //    //printf("  Sorting vertices by a bsp-tree.\n");
  //  }
  //  // Sort the points using a binary tree recursively.
  //  btree_sort(insertarray, in->numberofpoints, 0, xmin, xmax, ymin, ymax,
  //             zmin, zmax, 0);
  //  if (b->verbose) {
  //    //printf("  Number of tree nodes: %ld.\n", btreenode_list->objects);
  //    //printf("  Maximum tree node size: %d.\n", max_btreenode_size);
  //    //printf("  Maximum tree depth: %d.\n", max_btree_depth);
  //  }
  //  // Order the sorted points.
  //  ordervertices(insertarray, in->numberofpoints);
  //} else {
   /* if (b->verbose) {
      //printf("  Permuting vertices.\n"); 
    }*/
    // Randomize the point order.
    for (i = 0; i < arraysize; i++) {
      j = (int) randomnation(i + 1); // 0 <= j <= i;
      insertarray[i] = insertarray[j];
      insertarray[j] = pointtraverse();
    }
  //}

  //if (b->verbose) {
  //  //printf("  Incrementally inserting vertices.\n");
  //}

  // Form the DT by incremental flip Delaunay algorithm.
  success = incrflipdelaunay(NULL, insertarray, arraysize, true, b->plc,
                             0.0, NULL);

  //marktag
  /*if (b->btree) {
    point **pptary;
    for (i = 0; i < (int) btreenode_list->objects; i++) {
      pptary = (point **) fastlookup(btreenode_list, i);
      delete [] *pptary;
    }
    delete btreenode_list;
    btreenode_list = NULL;
  }

  delete [] insertarray;*/

  if (!success) {
    return 0l;
  } else {
    return hullsize;
  }
}

////                                                                       ////
////                                                                       ////
//// delaunay_cxx /////////////////////////////////////////////////////////////

//// surface_cxx //////////////////////////////////////////////////////////////
////                                                                       ////
////                                                                       ////

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// sinsertvertex()    Insert a vertex into a triangulation of a facet.       //
//                                                                           //
// The new point (p) will be located. Searching from 'splitsh'. If 'splitseg'//
// is not NULL, p is on a segment, no search is needed.                      //
//                                                                           //
// If 'cflag' is not TRUE, the triangulation may be not convex. Don't insert //
// p if it is found in outside.                                              //
//                                                                           //
// Comment: This routine assumes the 'abovepoint' of this facet has been set,//
// i.e., the routine getabovepoint() has been executed before it is called.  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

enum tetgenmesh::locateresult tetgenmesh::sinsertvertex(point insertpt, 
  face *splitsh, face *splitseg, bool bwflag, bool cflag)
{
  face *abfaces, *parysh, *pssub;
  face neighsh, newsh, casout, casin;
  face aseg, bseg, aoutseg, boutseg;
  face checkseg;
  triface neightet;
  point pa, pb, pc, *ppt;
  enum locateresult loc;
  REAL sign, ori, area;
  int n, s, i, j;

  if (splitseg != NULL) {
    spivot(*splitseg, *splitsh);
    loc = ONEDGE;
  } else {
    // Locate the point, '1' means the flag stop-at-segment is on. 
    loc = locatesub(insertpt, splitsh, 1, 0);
  }

  // Return if p lies on a vertex.
  if (loc == ONVERTEX) return loc;

  if (loc == OUTSIDE) {
    // Return if 'cflag' is not set.
    if (!cflag) return loc;
  }

  if (loc == ONEDGE) {
    if (splitseg == NULL) {
      // Do not split a segment.
      sspivot(*splitsh, checkseg);
      if (checkseg.sh != dummysh) return loc; // return ONSUBSEG;
      // Check if this edge is on the hull.
      spivot(*splitsh, neighsh);
      if (neighsh.sh == dummysh) {
        // A convex hull edge. The new point is on the hull.
        loc = OUTSIDE;
      }
    }
  }

  if (b->verbose > 1) {
    pa = sorg(*splitsh);
    pb = sdest(*splitsh);
    pc = sapex(*splitsh);
    //printf("    Insert point %d (%d, %d, %d) loc %d\n", pointmark(insertpt),
      //pointmark(pa), pointmark(pb), pointmark(pc), (int) loc);
  }

  // Does 'insertpt' lie on a segment?
  if (splitseg != NULL) {
    splitseg->shver = 0;
    pa = sorg(*splitseg);
    // Count the number of faces at segment [a, b].
    n = 0;
    neighsh = *splitsh;
    do {
      spivotself(neighsh);
      n++;
    } while ((neighsh.sh != dummysh) && (neighsh.sh != splitsh->sh));
    // n is at least 1.
    abfaces = new face[n];
    // Collect faces at seg [a, b].
    abfaces[0] = *splitsh;
    if (sorg(abfaces[0]) != pa) sesymself(abfaces[0]);
    for (i = 1; i < n; i++) {
      spivot(abfaces[i - 1], abfaces[i]);
      if (sorg(abfaces[i]) != pa) sesymself(abfaces[i]);
    }
  }

  // Initialize the cavity.
  if (loc == ONEDGE) {
    smarktest(*splitsh);
    caveshlist->newindex((void **) &parysh);
    *parysh = *splitsh;
    if (splitseg != NULL) {
      for (i = 1; i < n; i++) {
        smarktest(abfaces[i]);
        caveshlist->newindex((void **) &parysh);
        *parysh = abfaces[i];
      }
    } else {
      spivot(*splitsh, neighsh);
      if (neighsh.sh != dummysh) {
        smarktest(neighsh);
        caveshlist->newindex((void **) &parysh);
        *parysh = neighsh;
      }
    }
  } else if (loc == ONFACE) {
    smarktest(*splitsh);
    caveshlist->newindex((void **) &parysh);
    *parysh = *splitsh;
  } else { // loc == OUTSIDE;
    // This is only possible when T is convex.
    //assert(cflag); // SELF_CHECK
    // Adjust 'abovepoint' to be above the 'splitsh'. 2009-07-21.
    ori = orient3d(sorg(*splitsh), sdest(*splitsh), sapex(*splitsh), 
                   abovepoint);
    //assert(ori != 0);
    if (ori > 0) {
      sesymself(*splitsh);
    }
    // Assume p is on top of the edge ('splitsh'). Find a right-most edge
    //   which is visible by p.
    neighsh = *splitsh;
    while (1) {
      senext2self(neighsh);
      spivot(neighsh, casout);
      if (casout.sh == dummysh) {
        // A convex hull edge. Is it visible by p.
        pa = sorg(neighsh);
        pb = sdest(neighsh);
        ori = orient3d(pa, pb, abovepoint, insertpt);
        if (ori < 0) {
          *splitsh = neighsh; // Update 'splitsh'.
        } else {
          break; // 'splitsh' is the right-most visible edge.
        }
      } else {
        if (sorg(casout) != sdest(neighsh)) sesymself(casout);
        neighsh = casout;
      }
    }
    // Create new triangles for all visible edges of p (from right to left).
    casin.sh = dummysh;  // No adjacent face at right.
    pa = sorg(*splitsh);
    pb = sdest(*splitsh);
    while (1) {
      // Create a new subface on top of the (visible) edge.
      makeshellface(subfaces, &newsh); 
      // setshvertices(newsh, pb, pa, insertpt);
      setsorg(newsh, pb);
      setsdest(newsh, pa);
      setsapex(newsh, insertpt);
      setshellmark(newsh, shellmark(*splitsh));
      if (b->quality && varconstraint) {
        area = areabound(*splitsh);
        setareabound(newsh, area);
      }
      // Connect the new subface to the bottom subfaces.
      sbond1(newsh, *splitsh);
      sbond1(*splitsh, newsh);
      // Connect the new subface to its right-adjacent subface.
      if (casin.sh != dummysh) {
        senext(newsh, casout);
        sbond1(casout, casin);
        sbond1(casin, casout);
      }
      // The left-adjacent subface has not been created yet.
      senext2(newsh, casin);
      // Add the new face into list.
      smarktest(newsh);
      caveshlist->newindex((void **) &parysh);
      *parysh = newsh;
      // Move to the convex hull edge at the left of 'splitsh'.
      neighsh = *splitsh;
      while (1) {
        senextself(neighsh);
        spivot(neighsh, casout);
        if (casout.sh == dummysh) {
          *splitsh = neighsh;
          break;
        }
        if (sorg(casout) != sdest(neighsh)) sesymself(casout);
        neighsh = casout;
      }
      // A convex hull edge. Is it visible by p.
      pa = sorg(*splitsh);
      pb = sdest(*splitsh);
      ori = orient3d(pa, pb, abovepoint, insertpt);
      if (ori >= 0) break;
    }
  }

  // Form the Bowyer-Watson cavity.
  for (i = 0; i < (int) caveshlist->objects; i++) {
    parysh = (face *) fastlookup(caveshlist, i);
    for (j = 0; j < 3; j++) {
      sspivot(*parysh, checkseg);
      if (checkseg.sh == dummysh) {
        spivot(*parysh, neighsh);
        if (neighsh.sh != dummysh) {
          if (!smarktested(neighsh)) {
            if (bwflag) {
              pa = sorg(neighsh);
              pb = sdest(neighsh);
              pc = sapex(neighsh);
              sign = incircle3d(pa, pb, pc, insertpt);
              if (sign < 0) {
                smarktest(neighsh);
                caveshlist->newindex((void **) &pssub);
                *pssub = neighsh;
              }
            } else {
              sign = 1; // A boundary edge.
            }
          } else {
            sign = -1; // Not a boundary edge.
          }
        } else {
          if (loc == OUTSIDE) {
            // It is a boundary edge if it does not contain insertp.
            if ((sorg(*parysh)==insertpt) || (sdest(*parysh)==insertpt)) {
              sign = -1; // Not a boundary edge.
            } else {
              sign = 1; // A boundary edge.
            }
          } else {
            sign = 1; // A boundary edge.
          }
        }
      } else {
        sign = 1; // A segment!
      }
      if (sign >= 0) {
        // Add a boundary edge.
        caveshbdlist->newindex((void **) &pssub);
        *pssub = *parysh;
      }
      senextself(*parysh);
    }
  }

  // Creating new subfaces.
  for (i = 0; i < (int) caveshbdlist->objects; i++) {
    parysh = (face *) fastlookup(caveshbdlist, i);
    sspivot(*parysh, checkseg);
    if ((parysh->shver & 01) != 0) sesymself(*parysh);
    pa = sorg(*parysh);
    pb = sdest(*parysh);
    // Create a new subface.
    makeshellface(subfaces, &newsh); 
    // setshvertices(newsh, pa, pb, insertpt);
    setsorg(newsh, pa);
    setsdest(newsh, pb);
    setsapex(newsh, insertpt);
    setshellmark(newsh, shellmark(*parysh));
    if (b->quality && varconstraint) {
      area = areabound(*parysh);
      setareabound(newsh, area);
    }
    // Connect newsh to outer subfaces.
    spivot(*parysh, casout);
    if (casout.sh != dummysh) {
      if (casout.sh != parysh->sh) { // It is not self-bonded.
        casin = casout;
        if (checkseg.sh != dummysh) {
          spivot(casin, neighsh);
          while (neighsh.sh != parysh->sh) {
            casin = neighsh;
            spivot(casin, neighsh);
          }
        }
        sbond1(newsh, casout);
        sbond1(casin, newsh);
      } else {
        // This side is empty.
      }
    } else {
      // This is a hull side. Save it in dummysh[0] (it will be used by 
      //   the routine locatesub()). 2009-07-20.
      dummysh[0] = sencode(newsh);
    }
    if (checkseg.sh != dummysh) {
      ssbond(newsh, checkseg);
    }
    // Connect oldsh <== newsh (for connecting adjacent new subfaces).
    sbond1(*parysh, newsh);
  }

  // Set a handle for searching.
  // recentsh = newsh;

  // Connect adjacent new subfaces together.
  for (i = 0; i < (int) caveshbdlist->objects; i++) {
    // Get an old subface at edge [a, b].
    parysh = (face *) fastlookup(caveshbdlist, i);
    sspivot(*parysh, checkseg);
    spivot(*parysh, newsh); // The new subface [a, b, p].
    senextself(newsh); // At edge [b, p].
    spivot(newsh, neighsh);
    if (neighsh.sh == dummysh) {
      // Find the adjacent new subface at edge [b, p].
      pb = sdest(*parysh);
      neighsh = *parysh;
      while (1) {
        senextself(neighsh);
        spivotself(neighsh);
        if (neighsh.sh == dummysh) break;
        if (!smarktested(neighsh)) break;
        if (sdest(neighsh) != pb) sesymself(neighsh);
      }
      if (neighsh.sh != dummysh) {
        // Now 'neighsh' is a new subface at edge [b, #].
        if (sorg(neighsh) != pb) sesymself(neighsh);
        //assert(sorg(neighsh) == pb); // SELF_CHECK
        //assert(sapex(neighsh) == insertpt); // SELF_CHECK
        senext2self(neighsh); // Go to the open edge [p, b].
        spivot(neighsh, casout); // SELF_CHECK
        //assert(casout.sh == dummysh); // SELF_CHECK
        sbond(newsh, neighsh);
      } else {
        //assert(loc == OUTSIDE); // SELF_CHECK
        // It is a hull edge. 2009-07-21
        dummysh[0] = sencode(newsh);
      }
    }
    spivot(*parysh, newsh); // The new subface [a, b, p].
    senext2self(newsh); // At edge [p, a].
    spivot(newsh, neighsh);
    if (neighsh.sh == dummysh) {
      // Find the adjacent new subface at edge [p, a].
      pa = sorg(*parysh);
      neighsh = *parysh;
      while (1) {
        senext2self(neighsh);
        spivotself(neighsh);
        if (neighsh.sh == dummysh) break;
        if (!smarktested(neighsh)) break;
        if (sorg(neighsh) != pa) sesymself(neighsh);
      }
      if (neighsh.sh != dummysh) {
        // Now 'neighsh' is a new subface at edge [#, a].
        if (sdest(neighsh) != pa) sesymself(neighsh);
        //assert(sdest(neighsh) == pa); // SELF_CHECK
        //assert(sapex(neighsh) == insertpt); // SELF_CHECK
        senextself(neighsh); // Go to the open edge [a, p].
        spivot(neighsh, casout); // SELF_CHECK
        //assert(casout.sh == dummysh); // SELF_CHECK
        sbond(newsh, neighsh);
      } else {
        //assert(loc == OUTSIDE); // SELF_CHECK
        // It is a hull edge. 2009-07-21
        dummysh[0] = sencode(newsh);
      }
    }
  }

  if (splitseg != NULL) {
    // Split the segment [a, b].
    aseg = *splitseg;
    pa = sorg(aseg);
    pb = sdest(aseg);
    if (b->verbose > 1) {
      //printf("    Split seg (%d, %d) by %d.\n", pointmark(pa), pointmark(pb), 
        //pointmark(insertpt));
    }
    // Insert the new point p.
    makeshellface(subsegs, &bseg);
    // setshvertices(bseg, insertpt, pb, NULL);
    setsorg(bseg, insertpt);
    setsdest(bseg, pb);
    setsapex(bseg, NULL);
    setsdest(aseg, insertpt);
    setshellmark(bseg, shellmark(aseg));
    // This is done outside this routine (at where newpt was created).
    // setpoint2sh(insertpt, sencode(aseg));
    if (b->quality && varconstraint) {
      setareabound(bseg, areabound(aseg));
    }
    // Update the point-to-seg map.
    setpoint2seg(pb, sencode(bseg));
    setpoint2seg(insertpt, sencode(bseg));
    // Connect [p, b]<->[b, #].
    senext(aseg, aoutseg);
    spivotself(aoutseg);
    if (aoutseg.sh != dummysh) {
      senext(bseg, boutseg);
      sbond(boutseg, aoutseg);
    }
    // Connect [a, p] <-> [p, b].
    senext(aseg, aoutseg);
    senext2(bseg, boutseg);
    sbond(aoutseg, boutseg);
    // Connect subsegs [a, p] and [p, b] to the true new subfaces.
    for (i = 0; i < n; i++) {
      spivot(abfaces[i], newsh); // The faked new subface.
      if (sorg(newsh) != pa) sesymself(newsh);
      senext2(newsh, neighsh); // The edge [p, a] in newsh
      spivot(neighsh, casout);
      ssbond(casout, aseg);
      senext(newsh, neighsh); // The edge [b, p] in newsh
      spivot(neighsh, casout);
      ssbond(casout, bseg);
    }
    if (n > 1) {
      // Create the two face rings at [a, p] and [p, b].
      for (i = 0; i < n; i++) {
        spivot(abfaces[i], newsh); // The faked new subface.
        if (sorg(newsh) != pa) sesymself(newsh);
        spivot(abfaces[(i + 1) % n], neighsh); // The next faked new subface.
        if (sorg(neighsh) != pa) sesymself(neighsh);
        senext2(newsh, casout); // The edge [p, a] in newsh.
        senext2(neighsh, casin); // The edge [p, a] in neighsh.
        spivotself(casout);
        spivotself(casin);
        sbond1(casout, casin); // Let the i's face point to (i+1)'s face.
        senext(newsh, casout); // The edge [b, p] in newsh.
        senext(neighsh, casin); // The edge [b, p] in neighsh.
        spivotself(casout);
        spivotself(casin);
        sbond1(casout, casin);
      }
    } else { 
      // Only one subface contains this segment.
      // //assert(n == 1);
      spivot(abfaces[0], newsh);  // The faked new subface.
      if (sorg(newsh) != pa) sesymself(newsh);
      senext2(newsh, casout); // The edge [p, a] in newsh.
      spivotself(casout);
      sdissolve(casout); // Disconnect to faked subface.
      senext(newsh, casout); // The edge [b, p] in newsh.
      spivotself(casout);
      sdissolve(casout); // Disconnect to faked subface.
    }
    // Delete the faked new subfaces.
    for (i = 0; i < n; i++) {
      spivot(abfaces[i], newsh); // The faked new subface.
      shellfacedealloc(subfaces, newsh.sh);
    }
    if (checksubsegs) {
      // Add two subsegs into stack (for recovery).
      if (!sinfected(aseg)) {
        s = randomnation(subsegstack->objects + 1);
        subsegstack->newindex((void **) &parysh);
        *parysh = * (face *) fastlookup(subsegstack, s);
        sinfect(aseg); 
        parysh = (face *) fastlookup(subsegstack, s);
        *parysh = aseg;
      }
      //assert(!sinfected(bseg)); // SELF_CHECK
      s = randomnation(subsegstack->objects + 1);
      subsegstack->newindex((void **) &parysh);
      *parysh = * (face *) fastlookup(subsegstack, s);
      sinfect(bseg);
      parysh = (face *) fastlookup(subsegstack, s);
      *parysh = bseg;
    }
   //marktag
	// delete [] abfaces;
  }

  if (checksubfaces) {
    // Add all new subfaces into list.
    for (i = 0; i < (int) caveshbdlist->objects; i++) {
      // Get an old subface at edge [a, b].
      parysh = (face *) fastlookup(caveshbdlist, i);
      spivot(*parysh, newsh); // The new subface [a, b, p].
      // Some new subfaces may get deleted (when 'splitseg' is a segment).
      if (!isdead(&newsh)) {
        if (b->verbose > 1) {
          //printf("      Queue a new subface (%d, %d, %d).\n",
            //pointmark(sorg(newsh)), pointmark(sdest(newsh)),
            //pointmark(sapex(newsh)));
        }
        subfacstack->newindex((void **) &pssub);
        *pssub = newsh;
      }
    }
  }

  // Update the point-to-subface map.
  for (i = 0; i < (int) caveshbdlist->objects; i++) {
    // Get an old subface at edge [a, b].
    parysh = (face *) fastlookup(caveshbdlist, i);
    spivot(*parysh, newsh); // The new subface [a, b, p].
    // Some new subfaces may get deleted (when 'splitseg' is a segment).
    if (!isdead(&newsh)) {
      ppt = (point *) &(newsh.sh[3]);
      for (j = 0; j < 3; j++) {
        setpoint2sh(ppt[j], sencode(newsh));
      }
    }
  }

  // Delete the old subfaces.
  for (i = 0; i < (int) caveshlist->objects; i++) {
    parysh = (face *) fastlookup(caveshlist, i);
    if (checksubfaces) {		 
      // Disconnect in the neighbor tets.
      for (j = 0; j < 2; j++) {		 
        stpivot(*parysh, neightet);		 
        if (neightet.tet != dummytet) {		 
          tsdissolve(neightet);		 
          // symself(neightet);		 
          // tsdissolve(neightet);		 
        }
        sesymself(*parysh);
      }
    }
    shellfacedealloc(subfaces, parysh->sh);
  }

  // Clean the working lists.
  caveshlist->restart();
  caveshbdlist->restart();

  return loc;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// formstarpolygon()    Form the star polygon of a point in facet.           //
//                                                                           //
// The polygon P is formed by all coplanar subfaces having 'pt' as a vertex. //
// P is bounded by segments, e.g, if no segments, P is the full star of pt.  //
//                                                                           //
// 'trilist' T returns the subfaces, it has one of such subfaces on input.   //
// In addition, if f is in T, then sapex(f) = p. 'vertlist' V are verts of P.//
// Topologically, T is the star of p; V and the edges of T are the link of p.//
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::formstarpolygon(point pt, list* trilist, list* vertlist)
{
  face steinsh, lnextsh, rnextsh;
  face checkseg;
  point pa, pb, pc, pd;
  int i;

  // Get a subface f containing p.
  steinsh = * (face *)(* trilist)[0];
  steinsh.shver = 0; // CCW
  // Let sapex(f) be p.
  for (i = 0; i < 3; i++) {
    if (sapex(steinsh) == pt) break;
    senextself(steinsh);
  }
  //assert(i < 3);
  // Add the edge f into list.
  * (face *)(* trilist)[0] = steinsh;
  pa = sorg(steinsh);
  pb = sdest(steinsh);
  if (vertlist != (list *) NULL) {
    // Add two verts a, b into V,
    vertlist->append(&pa);
    vertlist->append(&pb);
  }

  // Rotate edge pa to the left (CW) until meet pb or a segment.
  lnextsh = steinsh;
  pc = pa;
  do {
    senext2self(lnextsh);
    //assert(sorg(lnextsh) == pt);
    sspivot(lnextsh, checkseg);
    if (checkseg.sh != dummysh) break; // Do not cross a segment.
    // Get neighbor subface n (must exist).
    spivotself(lnextsh);
    if (lnextsh.sh == dummysh) break; // It's a hull edge.
    // Go to the edge ca opposite to p.
    if (sdest(lnextsh) != pt) sesymself(lnextsh);
    //assert(sdest(lnextsh) == pt);
    senext2self(lnextsh);
    // Add n (at edge ca) to T.
    trilist->append(&lnextsh);
    // Add edge ca to E.
    pc = sorg(lnextsh);
    if (pc == pb) break; // Rotate back.
    if (vertlist != (list *) NULL) {
      // Add vert c into V.
      vertlist->append(&pc);
    }
  } while (true);

  if (pc != pb) {
    // Rotate edge bp to the right (CCW) until meet a segment.
    rnextsh = steinsh;
    do {
      senextself(rnextsh);
      //assert(sdest(rnextsh) == pt);
      sspivot(rnextsh, checkseg);
      if (checkseg.sh != dummysh) break; // Do not cross a segment.
      // Get neighbor subface n (must exist).
      spivotself(rnextsh);
      if (rnextsh.sh == dummysh) break; // It's a hull edge.
      // Go to the edge bd opposite to p.
      if (sorg(rnextsh) != pt) sesymself(rnextsh);
      //assert(sorg(rnextsh) == pt);
      senextself(rnextsh);
      // Add n (at edge bd) to T.
      trilist->append(&rnextsh);
      // Add edge bd to E.
      pd = sdest(rnextsh);
      if (pd == pa) break; // Rotate back.
      if (vertlist != (list *) NULL) {
        // Add vert d into V.
        vertlist->append(&pd);
      }
    } while (true);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// About the 'abovepoint'                                                    //
//                                                                           //
// The 'abovepoint' of a facet is a point which is exactly non-coplanar with //
// the plane containing that facet.  With such an point, the 3D predicates:  //
// orient3d(), and insphere() can be used to substitute the corresponding 2D //
// siblings, e.g. orient2d(), and incircle().  Its location is not critical, //
// but floating-point accuracy is improved if it is nicely placed over the   //
// facet, not too close or too far away.                                     //
//                                                                           //
// We take the convention that the abovepoint of a facet always lies above   //
// the facet. By this convention, given three points a, b, and c in a facet, //
// we say c has the counterclockwise order with ab is corresponding to say   //
// that c is below the plane abp, where p is the lift point.                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// getfacetabovepoint()    Get a point above a plane pass through a facet.   //
//                                                                           //
// The calculcated point is saved in 'facetabovepointarray'. The 'abovepoint'//
// is set on return.                                                         //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

__device__ void tetgenmesh::getfacetabovepoint(face* facetsh)
{
  list *verlist, *trilist, *tetlist;
  triface adjtet;
  face symsh;
  point p1, p2, p3, pa;
  // enum locateresult loc;
  REAL smallcos, cosa;
  REAL largevol, volume;
  REAL v1[3], v2[3], len;
  int smallidx, largeidx;
  int shmark;
  int i, j;

  abovecount++;
  // Initialize working lists.
  //marktag
  //verlist = new list(sizeof(point *), NULL);
  //trilist = new list(sizeof(face), NULL);
  //tetlist = new list(sizeof(triface), NULL);

  // Get three pivotal points p1, p2, and p3 in the facet as a base triangle
  //   which is non-trivil and has good base angle (close to 90 degree).

  // p1 is chosen as the one which has the smallest index in pa, pb, pc.
  p1 = sorg(*facetsh);
  pa = sdest(*facetsh);
  if (pointmark(pa) < pointmark(p1)) p1 = pa;
  pa = sapex(*facetsh);
  if (pointmark(pa) < pointmark(p1)) p1 = pa;
  // Form the star polygon of p1.
  trilist->append(facetsh);
  formstarpolygon(p1, trilist, verlist);

  // Get the second pivotal point p2.
  p2 = * (point *)(* verlist)[0];
  // Get vector v1 = p1->p2.
  for (i = 0; i < 3; i++) v1[i] = p2[i] - p1[i];
  len = sqrt(dot(v1, v1));
  //assert(len > 0.0);  // p2 != p1.
  for (i = 0; i < 3; i++) v1[i] /= len;

  // Get the third pivotal point p3. p3 is chosen as the one in 'verlist'
  //   which forms an angle with v1 closer to 90 degree than others do.
  smallcos = 1.0; // The cosine value of 0 degree.
  smallidx = 1;   // Default value.
  for (i = 1; i < verlist->len(); i++) {
    p3 = * (point *)(* verlist)[i];
    for (j = 0; j < 3; j++) v2[j] = p3[j] - p1[j];
    len = sqrt(dot(v2, v2));
    if (len > 0.0) { // v2 is not too small.
      cosa = fabs(dot(v1, v2)) / len;
      if (cosa < smallcos) {
        smallidx = i;
        smallcos = cosa;
      }
    }
  }
  //assert(smallcos < 1.0); // p1->p3 != p1->p2.
  p3 = * (point *)(* verlist)[smallidx];
  verlist->clear();

  if (tetrahedrons->items > 0l) {
    // Get a tet having p1 as a vertex.
    point2tetorg(p1, adjtet);
    //assert(org(adjtet) == p1);
    if (adjtet.tet != dummytet) {
      // Get the star polyhedron of p1.
      tetlist->append(&adjtet);
      formstarpolyhedron(p1, tetlist, verlist, false);
    }
  }

  // Get the abovepoint in 'verlist'. It is the one form the largest valid
  //   volumw with the base triangle over other points in 'verlist.
  largevol = 0.0;
  largeidx = 0;
  for (i = 0; i < verlist->len(); i++) {
    pa = * (point *)(* verlist)[i];
    volume = orient3d(p1, p2, p3, pa);
    if (!iscoplanar(p1, p2, p3, pa, volume, b->epsilon * 1e+2)) {
      if (fabs(volume) > largevol) {
        largevol = fabs(volume);
        largeidx = i;
      }
    }
  }

  // Do we have the abovepoint?
  if (largevol > 0.0) {
    abovepoint = * (point *)(* verlist)[largeidx];
    if (b->verbose > 1) {
      ////printf("    Chosen abovepoint %d for facet %d.\n", pointmark(abovepoint),
       //      shellmark(*facetsh));
    }
  } else {
    // Calculate an abovepoint for this facet.
    facenormal(p1, p2, p3, v1, &len);
    if (len != 0.0) for (i = 0; i < 3; i++) v1[i] /= len;
    // Take the average edge length of the bounding box.
    len = (0.5*(xmax - xmin) + 0.5*(ymax - ymin) + 0.5*(zmax - zmin)) / 3.0;
    // Temporarily create a point. It will be removed by jettison();
    makepoint(&abovepoint);
    setpointtype(abovepoint, UNUSEDVERTEX);
    unuverts++;
    for (i = 0; i < 3; i++) abovepoint[i] = p1[i] + len * v1[i];
    if (b->verbose > 1) {
      ////printf("    Calculated abovepoint %d for facet %d.\n",
             //pointmark(abovepoint), shellmark(*facetsh));
    }
  }
  // Save the abovepoint in 'facetabovepointarray'.
  shmark = shellmark(*facetsh);
  facetabovepointarray[shmark] = abovepoint;
  
  //marktag
  //delete trilist;
  //delete tetlist;
  //delete verlist;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// incrflipdelaunaysub()    Create a DT from a 3D coplanar point set using   //
//                          the incremental flip algorithm.                  //
//                                                                           //
// Let T be the current Delaunay triangulation (of vertices of a facet F).   //
// 'shmark', the index of F in 'in->facetlist' (starts from 1).              //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

__device__ bool tetgenmesh::incrflipdelaunaysub(int shmark, REAL eps, list* ptlist,
  int holes, REAL* holelist, queue* flipque)
{
  face newsh, startsh;
  point *insertarray;
  point swappt;
  pbcdata *pd;
  enum locateresult loc;
  REAL det, area;
  bool aboveflag;
  int arraysize;
  int epscount;
  int fmarker;
  int idx, i, j, k;  

  // Get the point array (saved in 'ptlist').
  insertarray = (point *) ptlist->base;
  arraysize = ptlist->len();
  if (arraysize < 3) return false;

  // Do calculation of 'abovepoint' if number of points > 3.
  aboveflag = (arraysize > 3);

  // The initial triangulation T only has one triangle formed by 3 not
  //   cillinear points of the set V = 'insertarray'. The first point:
  //   a = insertarray[0].

  epscount = 0;
  while (true) {
  for (i = 1; i < arraysize; i++) {
    det = distance(insertarray[0], insertarray[i]);
    if (det > (longest * eps)) break;
  }
  if (i < arraysize) {
    // Swap to move b from index i to index 1.
    swappt = insertarray[i];
    insertarray[i] = insertarray[1];
    insertarray[1] = swappt;  
  }
  // Get the third point c, that is not collinear with a and b.
  for (i++; i < arraysize; i++) {
    if (!iscollinear(insertarray[0], insertarray[1], insertarray[i], eps))
      break;
  }
  if (i < arraysize) {
    // Swap to move c from index i to index 2.
    swappt = insertarray[i];
    insertarray[i] = insertarray[2];
    insertarray[2] = swappt;
    i = 3; // The next inserting point.
  } else {
    // The set of vertices is not good (or nearly degenerate).
    if ((eps == 0.0) || (epscount > 3)) {
      /*//printf("Warning:  Discard an invalid facet.\n");
      //printf("  #%d (%d, %d, %d", shmark, pointmark(insertarray[0]),
             pointmark(insertarray[1]), pointmark(insertarray[2]));*/
      if (ptlist->len() > 3) {
        ////printf(", ...");
      }
      ////printf(") looks like a line.\n");
      // terminatetetgen(1);
      return false;
    }
    // Decrease the eps, and continue to try.
    eps *= 1e-2;
    epscount++;
    continue;
  }
  break;
  } // while (true);

  // Create the initial triangle.
  makeshellface(subfaces, &newsh);
  setsorg(newsh, insertarray[0]);
  setsdest(newsh, insertarray[1]);
  setsapex(newsh, insertarray[2]);
  // Remeber the facet it belongs to.
  setshellmark(newsh, shmark);
  // Set vertex type be FREESUBVERTEX if it has no type yet.
  if (pointtype(insertarray[0]) == FREEVOLVERTEX) {
    setpointtype(insertarray[0], FREESUBVERTEX);
  }
  if (pointtype(insertarray[1]) == FREEVOLVERTEX) {
    setpointtype(insertarray[1], FREESUBVERTEX);
  }
  if (pointtype(insertarray[2]) == FREEVOLVERTEX) {
    setpointtype(insertarray[2], FREESUBVERTEX);
  }
  // Let 'dummysh' point to it (for point location).
  dummysh[0] = sencode(newsh);

  // Update the point-to-subface map.
  for (i = 0; i < 3; i++) {
    setpoint2sh(insertarray[i], sencode(newsh));
  }

  // Are there area constraints?
  if (b->quality && (in->facetconstraintlist != (REAL *) NULL)) {
    idx = in->facetmarkerlist[shmark - 1]; // The actual facet marker.
    for (k = 0; k < in->numberoffacetconstraints; k++) {
      fmarker = (int) in->facetconstraintlist[k * 2];
      if (fmarker == idx) {
        area = in->facetconstraintlist[k * 2 + 1];
        setareabound(newsh, area);
        break;
      }
    }
  }

  // Are there pbc conditions?
  if (checkpbcs) {
    idx = in->facetmarkerlist[shmark - 1]; // The actual facet marker.
    for (k = 0; k < in->numberofpbcgroups; k++) {
      pd = &subpbcgrouptable[k];
      for (j = 0; j < 2; j++) {
        if (pd->fmark[j] == idx) {
          setshellpbcgroup(newsh, k);
          pd->ss[j] = newsh;
        }
      }
    }
  }

  if (aboveflag) {
    // Compute the 'abovepoint' for orient3d().
    abovepoint = facetabovepointarray[shmark];
    if (abovepoint == (point) NULL) {
      getfacetabovepoint(&newsh);
    }
  }

  if (holes > 0) {
    // Project hole points onto the plane containing the facet.
    REAL prj[3];
    for (k = 0; k < holes; k++) {
      projpt2face(&(holelist[k * 3]), insertarray[0], insertarray[1],
                  insertarray[2], prj);
      for (j = 0; j < 3; j++) holelist[k * 3 + j] = prj[j];
    }
  }

  // Incrementally insert the rest of points into T.
  for (; i < arraysize; i++) {
    // Insert p_i.
    startsh.sh = dummysh;
    loc = sinsertvertex(insertarray[i], &startsh, NULL, true, true);
    // The point-to-subface map has been updated.
    // Set p_i's type FREESUBVERTEX if it has no type yet.
    if (pointtype(insertarray[i]) == FREEVOLVERTEX) {
      setpointtype(insertarray[i], FREESUBVERTEX);
    }
  }

  return true;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// finddirectionsub()    Find the first subface in a facet on the path from  //
//                       one point to another.                               //
//                                                                           //
// Finds the subface in the facet that intersects a line segment drawn from  //
// the origin of `searchsh' to the point `tend', and returns the result in   //
// `searchsh'.  The origin of `searchsh' does not change,  even though the   //
// subface returned may differ from the one passed in.                       //
//                                                                           //
// The return value notes whether the destination or apex of the found face  //
// is collinear with the two points in question.                             //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

enum tetgenmesh::finddirectionresult tetgenmesh::finddirectionsub(
  face* searchsh, point tend)
{
  face checksh;
  point startpoint, leftpoint, rightpoint;
  REAL leftccw, rightccw;
  REAL ori, sign;
  int leftflag, rightflag;

  startpoint = sorg(*searchsh);
  // Find the sign to simulate that abovepoint is 'above' the facet.
  adjustedgering(*searchsh, CCW);
  // Make sure 'startpoint' is the origin.
  if (sorg(*searchsh) != startpoint) senextself(*searchsh);
  rightpoint = sdest(*searchsh);
  leftpoint = sapex(*searchsh);
  ori = orient3d(startpoint, rightpoint, leftpoint, abovepoint);
  sign = ori > 0.0 ? -1 : 1;

  // Is `tend' to the left?
  ori = orient3d(tend, startpoint, abovepoint, leftpoint);
  leftccw = ori * sign;
  leftflag = leftccw > 0.0;
  // Is `tend' to the right?
  ori = orient3d(startpoint, tend, abovepoint, rightpoint);
  rightccw = ori * sign;
  rightflag = rightccw > 0.0;
  if (leftflag && rightflag) {
    // `searchsh' faces directly away from `tend'.  We could go left or
    //   right.  Ask whether it's a triangle or a boundary on the left.
    senext2(*searchsh, checksh);
    spivotself(checksh);
    if (checksh.sh == dummysh) {
      leftflag = 0;
    } else {
      rightflag = 0;
    }
  }
  while (leftflag) {
    // Turn left until satisfied.
    senext2self(*searchsh);
    spivotself(*searchsh);
    if (searchsh->sh == dummysh) {
      //printf("Internal error in finddirectionsub():  Unable to find a\n");
      //printf("  subface leading from %d to %d.\n", pointmark(startpoint),
            // pointmark(tend));
      //terminatetetgen(2);
		return;
    }
    if (sorg(*searchsh) != startpoint) sesymself(*searchsh);
    //assert(sorg(*searchsh) == startpoint);
    leftpoint = sapex(*searchsh);
    rightccw = leftccw;
    ori = orient3d(tend, startpoint, abovepoint, leftpoint);
    leftccw = ori * sign;
    leftflag = leftccw > 0.0;
  }
  while (rightflag) {
    // Turn right until satisfied.
    spivotself(*searchsh);
    if (searchsh->sh == dummysh) {
      //printf("Internal error in finddirectionsub():  Unable to find a\n");
      //printf("  subface leading from %d to %d.\n", pointmark(startpoint),
             //pointmark(tend));
      //terminatetetgen(2);
	  return;
    }
    if (sdest(*searchsh) != startpoint) sesymself(*searchsh);
    //assert(sdest(*searchsh) == startpoint);
    senextself(*searchsh);
    rightpoint = sdest(*searchsh);
    leftccw = rightccw;
    ori = orient3d(startpoint, tend, abovepoint, rightpoint);
    rightccw = ori * sign;
    rightflag = rightccw > 0.0;
  }
  if (leftccw == 0.0) {
    return LEFTCOLLINEAR;
  } else if (rightccw == 0.0) {
    return RIGHTCOLLINEAR;
  } else {
    return ACROSSEDGE;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// insertsubseg()    Create a subsegment and insert it between two subfaces. //
//                                                                           //
// The new subsegment ab is inserted at the edge of subface 'tri'.  If ab is //
// not a hull edge, it is inserted between two subfaces.  If 'tri' is a hull //
// face, the initial face ring of ab will be set only one face which is self-//
// bonded.  The final face ring will be constructed in 'unifysegments()'.    //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::insertsubseg(face* tri)
{
  face oppotri;
  face newsubseg;
  point pa, pb;
  REAL len;
  int e1, e2;
  int i;

  // Check if there's already a subsegment here.
  sspivot(*tri, newsubseg);
  if (newsubseg.sh == dummysh) {
    // Make new subsegment and initialize its vertices.
    makeshellface(subsegs, &newsubseg);
    pa = sorg(*tri);
    pb = sdest(*tri);
    setsorg(newsubseg, pa);
    setsdest(newsubseg, pb);
    // Are there length constraints?
    if (b->quality && (in->segmentconstraintlist != (REAL *) NULL)) {
      for (i = 0; i < in->numberofsegmentconstraints; i++) {
        e1 = (int) in->segmentconstraintlist[i * 3];
        e2 = (int) in->segmentconstraintlist[i * 3 + 1];
        if (((pointmark(pa) == e1) && (pointmark(pb) == e2)) ||
            ((pointmark(pa) == e2) && (pointmark(pb) == e1))) {
          len = in->segmentconstraintlist[i * 3 + 2];
          setareabound(newsubseg, len);
          break;
        }
      }
    }
    // Bond new subsegment to the two subfaces it is sandwiched between.
    ssbond(*tri, newsubseg);
    spivot(*tri, oppotri);
    // 'oppotri' might be "out space".
    if (oppotri.sh != dummysh) {
      ssbond(oppotri, newsubseg);
    } /* else {
      // Outside! Bond '*tri' to itself.
      sbond(*tri, *tri);
    } */
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// scoutsegmentsub()    Scout the first triangle on the path from one point  //
//                      to another, and check for completion (reaching the   //
//                      second point), a collinear point,or the intersection //
//                      of two segments.                                     //
//                                                                           //
// Returns true if the entire segment is successfully inserted, and false if //
// the job must be finished by constrainededge().                            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::scoutsegmentsub(face* searchsh, point tend)
{
  face newsubseg;
  face crosssub, crosssubseg;
  point leftpoint, rightpoint;
  enum finddirectionresult collinear;

  collinear = finddirectionsub(searchsh, tend);
  rightpoint = sdest(*searchsh);
  leftpoint = sapex(*searchsh);
  if (rightpoint == tend || leftpoint == tend) {
    // The segment is already an edge.
    if (leftpoint == tend) {
      senext2self(*searchsh);
    }
    // Insert a subsegment.
    insertsubseg(searchsh);
    return true;
  } else if (collinear == LEFTCOLLINEAR) {
    // We've collided with a vertex between the segment's endpoints.
    // Make the collinear vertex be the triangle's origin.
    senextself(*searchsh); // lprevself(*searchtri);
    // Insert a subsegment.
    insertsubseg(searchsh);
    // Insert the remainder of the segment.
    return scoutsegmentsub(searchsh, tend);
  } else if (collinear == RIGHTCOLLINEAR) {
    // We've collided with a vertex between the segment's endpoints.
    // Insert a subsegment.
    insertsubseg(searchsh);
    // Make the collinear vertex be the triangle's origin.
    senextself(*searchsh); // lnextself(*searchtri);
    // Insert the remainder of the segment.
    return scoutsegmentsub(searchsh, tend);
  } else {
    senext(*searchsh, crosssub); // lnext(*searchtri, crosstri);
    // Check for a crossing segment.
    sspivot(crosssub, crosssubseg);
#ifdef SELF_CHECK
    //assert(crosssubseg.sh == dummysh);
#endif
    return false;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// flipedgerecursive()    Flip an edge.                                      //
//                                                                           //
// This is a support routine for inserting segments into a CDT.              //
//                                                                           //
// Let 'flipedge' be ab, and two triangles abc, abd share at it.  ab may not //
// flipable if the four vertices a, b, c, and d are non-convex. If it is the //
// case, recursively flip ad or bd. Return when ab is flipped.               //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::flipedgerecursive(face* flipedge, queue* flipqueue)
{
  face fixupsh;
  point pa, pb, pc, pd;
  REAL oria, orib;
  bool doflip;

  pa = sorg(*flipedge);
  pb = sdest(*flipedge);
  pc = sapex(*flipedge);
  do {
    spivot(*flipedge, fixupsh);    
    pd = sapex(fixupsh);
    oria = orient3d(pc, pd, abovepoint, pa);
    orib = orient3d(pc, pd, abovepoint, pb);
    doflip = (oria * orib < 0.0);
    if (doflip) {
      // Flip the edge (a, b) away.      
      flip22sub(flipedge, flipqueue);
      // Fix flipedge on edge e (c, d).
      findedge(flipedge, pc, pd);
    } else {
      // ab is unflipable. Get the next edge (bd, or da) to flip.
      if (sorg(fixupsh) != pb) sesymself(fixupsh);
      //assert(sdest(fixupsh) == pa);
      if (fabs(oria) > fabs(orib)) {
        // acd has larger area. Choose da.
        senextself(fixupsh);
      } else {
        // bcd has larger area. Choose bd.
        senext2self(fixupsh);
      }
      // Flip the edge.
      flipedgerecursive(&fixupsh, flipqueue);
    }
  } while (!doflip);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// constrainededge()    Force a segment into a CDT.                          //
//                                                                           //
// The segment s is recovered by flipping away the edges it intersects, and  //
// triangulating the polygons that form on each side of it.                  //
//                                                                           //
// Generates a single subsegment connecting `tstart' to `tend'. The triangle //
// `startsh' has `tstart' as its origin.                                     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::constrainededge(face* startsh, point tend, queue* flipqueue)
{
  point tstart, tright, tleft;
  REAL rori, lori;
  bool collision;

  tstart = sorg(*startsh);
  do {
    // Loop edges oppo to tstart until find one crosses the segment.
    do {
      tright = sdest(*startsh);
      tleft = sapex(*startsh);
      // Is edge (tright, tleft) corss the segment.
      rori = orient3d(tstart, tright, abovepoint, tend);
      collision = (rori == 0.0);
      if (collision) break; // tright is on the segment.
      lori = orient3d(tstart, tleft, abovepoint, tend);
      collision = (lori == 0.0);
      if (collision) { //  tleft is on the segment.
        senext2self(*startsh);
        break;
      }
      if (rori * lori < 0.0) break; // Find the crossing edge.
      // Both points are at one side of the segment. 
      finddirectionsub(startsh, tend);
    } while (true);
    if (collision) break;
    // Get the neighbor face at edge e (tright, tleft).
    senextself(*startsh);
    // Flip the crossing edge.
    flipedgerecursive(startsh, flipqueue);
    // After flip, sorg(*startsh) == tstart.
    //assert(sorg(*startsh) == tstart);
  } while (sdest(*startsh) != tend);

  // Insert a subsegment to make the segment permanent.
  insertsubseg(startsh);
  // If there was a collision with an interceding vertex, install another
  //   segment connecting that vertex with endpoint2.
  if (collision) {
    // Insert the remainder of the segment.
    if (!scoutsegmentsub(startsh, tend)) {
      constrainededge(startsh, tend, flipqueue);
    }
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// recoversegment()    Recover a segment in the surface triangulation.       //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::recoversegment(point tstart, point tend, queue* flipqueue)
{
  face searchsh;

  if (b->verbose > 2) {
    //printf("    Insert seg (%d, %d).\n", pointmark(tstart), pointmark(tend));
  }

  // Find a triangle whose origin is the segment's first endpoint.
  point2shorg(tstart, searchsh);
  // Scout the segment and insert it if it is found.
  if (scoutsegmentsub(&searchsh, tend)) {
    // The segment was easily inserted.
    return;
  }  
  // Insert the segment into the triangulation by flips.
  constrainededge(&searchsh, tend, flipqueue);
  // Some edges may need flipping.
  lawson(flipqueue);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// infecthullsub()    Virally infect all of the triangles of the convex hull //
//                    that are not protected by subsegments.                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::infecthullsub(memorypool* viri)
{
  face hulltri, nexttri, starttri;
  face hullsubseg;
  shellface **deadshellface;

  // Find a triangle handle on the hull.
  hulltri.sh = dummysh;
  hulltri.shver = 0;
  spivotself(hulltri);
  adjustedgering(hulltri, CCW);
  // Remember where we started so we know when to stop.
  starttri = hulltri;
  // Go once counterclockwise around the convex hull.
  do {
    // Ignore triangles that are already infected.
    if (!sinfected(hulltri)) {
      // Is the triangle protected by a subsegment?
      sspivot(hulltri, hullsubseg);
      if (hullsubseg.sh == dummysh) {
        // The triangle is not protected; infect it.
        if (!sinfected(hulltri)) {
          sinfect(hulltri);
          deadshellface = (shellface **) viri->alloc();
          *deadshellface = hulltri.sh;
        }
      } 
    }
    // To find the next hull edge, go clockwise around the next vertex.
    senextself(hulltri); 
    spivot(hulltri, nexttri);
    while (nexttri.sh != dummysh) {
      if (sorg(nexttri) != sdest(hulltri)) {
        sesymself(nexttri);
      }
      senext(nexttri, hulltri);
      spivot(hulltri, nexttri);
    }
  } while (hulltri != starttri);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// plaguesub()    Spread the virus from all infected triangles to any        //
//                neighbors not protected by subsegments.  Delete all        //
//                infected triangles.                                        //
//                                                                           //
// This is the procedure that actually creates holes and concavities.        //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::plaguesub(memorypool* viri)
{
  face testtri, neighbor, ghostsh;
  face neighborsubseg;
  shellface **virusloop;
  shellface **deadshellface;
  point *ppt;
  int i, j;

  // Loop through all the infected triangles, spreading the virus to
  //   their neighbors, then to their neighbors' neighbors.
  viri->traversalinit();
  virusloop = (shellface **) viri->traverse();
  while (virusloop != (shellface **) NULL) {
    testtri.sh = *virusloop;
    // Check each of the triangle's three neighbors.
    for (i = 0; i < 3; i++) {
      // Find the neighbor.
      spivot(testtri, neighbor);
      // Check for a subsegment between the triangle and its neighbor.
      sspivot(testtri, neighborsubseg);
      // Check if the neighbor is nonexistent or already infected.
      if ((neighbor.sh == dummysh) || sinfected(neighbor)) {
        if (neighborsubseg.sh != dummysh) {
          // There is a subsegment separating the triangle from its
          //   neighbor, but both triangles are dying, so the subsegment
          //   dies too.
          shellfacedealloc(subsegs, neighborsubseg.sh);
          if (neighbor.sh != dummysh) {
            // Make sure the subsegment doesn't get deallocated again
            //   later when the infected neighbor is visited.
            ssdissolve(neighbor);
          }
        }
      } else {                   // The neighbor exists and is not infected.
        if (neighborsubseg.sh == dummysh) {
          // There is no subsegment protecting the neighbor, so the
          //   neighbor becomes infected.
          sinfect(neighbor);
          // Ensure that the neighbor's neighbors will be infected.
          deadshellface = (shellface **) viri->alloc();
          *deadshellface = neighbor.sh;
        } else {               // The neighbor is protected by a subsegment.
          // Remove this triangle from the subsegment.
          ssbond(neighbor, neighborsubseg);
          // Update the point-to-subface map. 2009-07-21.
          ppt = (point *) &(neighbor.sh[3]);
          for (j = 0; j < 3; j++) {
            setpoint2sh(ppt[j], sencode(neighbor));
          }
        }
      }
      senextself(testtri);
    }
    virusloop = (shellface **) viri->traverse();
  }

  ghostsh.sh = dummysh; // A handle of outer space.
  viri->traversalinit();
  virusloop = (shellface **) viri->traverse();
  while (virusloop != (shellface **) NULL) {
    testtri.sh = *virusloop;
    // Record changes in the number of boundary edges, and disconnect
    //   dead triangles from their neighbors. 
    for (i = 0; i < 3; i++) {
      spivot(testtri, neighbor);
      if (neighbor.sh != dummysh) {
        // Disconnect the triangle from its neighbor.
        // sdissolve(neighbor);
        sbond(neighbor, ghostsh);
      }
      senextself(testtri);
    }
    // Return the dead triangle to the pool of triangles.
    shellfacedealloc(subfaces, testtri.sh);
    virusloop = (shellface **) viri->traverse();
  }
  // Empty the virus pool.
  viri->restart();
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// carveholessub()    Find the holes and infect them.  Find the area         //
//                    constraints and infect them.  Infect the convex hull.  //
//                    Spread the infection and kill triangles.  Spread the   //
//                    area constraints.                                      //
//                                                                           //
// This routine mainly calls other routines to carry out all these functions.//
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::carveholessub(int holes, REAL* holelist, memorypool *viri)
{
  face searchtri, triangleloop;
  shellface **holetri;
  enum locateresult intersect;
  int i;

  // Mark as infected any unprotected triangles on the boundary.
  //   This is one way by which concavities are created.
  infecthullsub(viri);

  if (holes > 0) {
    // Infect each triangle in which a hole lies.
    for (i = 0; i < 3 * holes; i += 3) {
      // Ignore holes that aren't within the bounds of the mesh.
      if ((holelist[i] >= xmin) && (holelist[i] <= xmax)
          && (holelist[i + 1] >= ymin) && (holelist[i + 1] <= ymax)
          && (holelist[i + 2] >= zmin) && (holelist[i + 2] <= zmax)) {
        // Start searching from some triangle on the outer boundary.
        searchtri.sh = dummysh;
        // Find a triangle that contains the hole.
        intersect = locatesub(&holelist[i], &searchtri, 0, 0.0);
        if ((intersect != OUTSIDE) && (!sinfected(searchtri))) {
          // Infect the triangle.  This is done by marking the triangle
          //   as infected and including the triangle in the virus pool.
          sinfect(searchtri);
          holetri = (shellface **) viri->alloc();
          *holetri = searchtri.sh;
        }
      }
    }
  }

  if (viri->items > 0) {
    // Carve the holes and concavities.
    plaguesub(viri);
  }
  // The virus pool should be empty now.
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// triangulate()    Triangulate a PSLG into a CDT.                           //
//                                                                           //
// A Planar Straight Line Graph (PSLG) P is actually a 2D polygonal region,  //
// possibly contains holes, segments and vertices in its interior. P is tri- //
// angulated into a set of _subfaces_ forming a CDT of P.                    //
//                                                                           //
// The vertices and segments of P are found in 'ptlist' and 'conlist', resp- //
// ectively. 'holelist' contains a list of hole points. 'shmark' will be set //
// to all subfaces of P.                                                     //
//                                                                           //
// The CDT is created directly in the pools 'subfaces' and 'subsegs'. It can //
// be retrived by a broadth-first searching starting from 'dummysh[0]'(debug //
// function 'outsurfmesh()' does it).                                        //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::triangulate(int shmark, REAL eps, list* ptlist, list* conlist,
  int holes, REAL* holelist, memorypool* viri, queue* flipqueue)
{
  face newsh;
  point *cons;
  int i;

  if (b->verbose > 1) {
    //printf("    %d vertices, %d segments", ptlist->len(), conlist->len());
    if (holes > 0) {
      //printf(", %d holes", holes);
    }
    //printf(", shmark: %d.\n", shmark);
  }

  // Create the DT of V by the 2D incremental flip algorithm.
  if (incrflipdelaunaysub(shmark, eps, ptlist, holes, holelist, flipqueue)) {
    // Recover boundary edges.
    if (ptlist->len() > 3) {
      // Insert segments into the DT.
      for (i = 0; i < conlist->len(); i++) {
        cons = (point *)(* conlist)[i];
        recoversegment(cons[0], cons[1], flipqueue);        
      }
      // Carve holes and concavities.
      carveholessub(holes, holelist, viri);
    } else if (ptlist->len() == 3) {
      // Insert 3 segments directly.
      newsh.sh = dummysh;
      newsh.shver = 0;
      spivotself(newsh);
      for (i = 0; i < 3; i++) {
        insertsubseg(&newsh);
        senextself(newsh);
      }
    } else if (ptlist->len() == 2) {
      // This facet is actually a segment. It is not support by the mesh data
      //   strcuture. Hence the segment will not be maintained in the mesh.
      //   However, during segment recovery, the segment can be processed.
      cons = (point *)(* conlist)[0];
      makeshellface(subsegs, &newsh);
      setsorg(newsh, cons[0]);
      setsdest(newsh, cons[1]);
    }
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// retrievenewsubs()    Retrieve newly created subfaces.                     //
//                                                                           //
// The new subfaces created by triangulate() can be found by a broadth-first //
// searching starting from 'dummysh[0]'.                                     //
//                                                                           //
// 'newshlist' (empty on input) returns the retrieved subfaces. Each edge on //
// the hull is bound to 'dummysh' and protected by a segment. If 'removeseg' //
// is TRUE, the segment is removed.                                          //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::retrievenewsubs(list* newshlist, bool removeseg)
{
  face startsh, neighsh;
  face deadseg;
  int i, j;

  // The first new subface is found at dummysh[0].
  startsh.sh = dummysh;
  startsh.shver = 0;
  spivotself(startsh);
  //assert(startsh.sh != dummysh);
  sinfect(startsh);
  newshlist->append(&startsh);

  // Find the rest of new subfaces by a broadth-first searching.
  for (i = 0; i < newshlist->len(); i++) {
    // Get a new subface s.
    startsh = * (face *)(* newshlist)[i];
    for (j = 0; j < 3; j++) {
      spivot(startsh, neighsh);
      if (neighsh.sh != dummysh) {
        if (!sinfected(neighsh)) {
          // Discovered a new subface.
          sinfect(neighsh);
          newshlist->append(&neighsh);
        }
      } else {
        // Found a boundary edge. 
        if (removeseg) {
          // This side of s may be protected by a segment.
          sspivot(startsh, deadseg);
          if (deadseg.sh != dummysh) {
            // Detach it from s.
            ssdissolve(startsh);
            // Delete the segment.
            shellfacedealloc(subsegs, deadseg.sh);
          }
        }
      }
      senextself(startsh);
    }
  }
  for (i = 0; i < newshlist->len(); i++) {
    startsh = * (face *)(* newshlist)[i];
    suninfect(startsh);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// unifysegments()    Unify identical segments and build facet connections.  //
//                                                                           //
// After creating the surface mesh. Each facet has its own segments.  There  //
// are duplicated segments between adjacent facets.  This routine has three  //
// purposes:                                                                 //
//   (1) identify the set of segments which have the same endpoints and      //
//       unify them into one segment, remove redundant ones;                 //
//   (2) create the face rings of the unified segments, hence setup the      //
//       connections between facets; and                                     //
//   (3) set a unique marker (1-based) for each segment.                     //
// On finish, each segment is unique and the face ring around it (right-hand //
// rule) is constructed. The connections between facets-facets are setup.    //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::unifysegments()
{
  list *sfacelist;
  shellface **facesperverlist;
  face subsegloop, testseg;
  face sface, sface1, sface2;
  point torg, tdest;
  REAL da1, da2;
  int *idx2facelist;
  int segmarker;
  int idx, k, m;

  if (b->verbose > 0) {
    //printf("  Unifying segments.\n");
  }

  // Compute a mapping from indices of vertices to subfaces.
  makesubfacemap(idx2facelist, facesperverlist);
  // Initialize 'sfacelist' for constructing the face link of each segment.
  sfacelist = new list(sizeof(face), NULL); 
  
  segmarker = 1;
  subsegs->traversalinit();
  subsegloop.sh = shellfacetraverse(subsegs);
  while (subsegloop.sh != (shellface *) NULL) {
    subsegloop.shver = 0; // For sure.
    torg = sorg(subsegloop);
    tdest = sdest(subsegloop);
    idx = pointmark(torg) - in->firstnumber;
    // Loop through the set of subfaces containing 'torg'.  Get all the
    //   subfaces containing the edge (torg, tdest). Save and order them
    //   in 'sfacelist', the ordering is defined by the right-hand rule
    //   with thumb points from torg to tdest.
    for (k = idx2facelist[idx]; k < idx2facelist[idx + 1]; k++) {
      sface.sh = facesperverlist[k];
      sface.shver = 0;
      // sface may be died due to the removing of duplicated subfaces.
      if (!isdead(&sface) && isfacehasedge(&sface, torg, tdest)) {
        // 'sface' contains this segment.
        findedge(&sface, torg, tdest);
        // Save it in 'sfacelist'.
        if (sfacelist->len() < 2) {
          sfacelist->append(&sface);
        } else {
          for (m = 0; m < sfacelist->len() - 1; m++) {
            sface1 = * (face *)(* sfacelist)[m];
            sface2 = * (face *)(* sfacelist)[m + 1];
            da1 = facedihedral(torg, tdest, sapex(sface1), sapex(sface));
            da2 = facedihedral(torg, tdest, sapex(sface1), sapex(sface2));
            if (da1 < da2) {
              break;  // Insert it after m.
            }
          }
          sfacelist->insert(m + 1, &sface);
        }
      }
    }
    if (b->verbose > 1) {
      //printf("    Identifying %d segments of (%d  %d).\n", sfacelist->len(),
             //pointmark(torg), pointmark(tdest));
    }
    // Set the connection between this segment and faces containing it,
    //   at the same time, remove redundant segments.
    for (k = 0; k < sfacelist->len(); k++) {
      sface = *(face *)(* sfacelist)[k];
      sspivot(sface, testseg);
      // If 'testseg' is not 'subsegloop', it is a redundant segment that
      //   needs be removed. BE CAREFUL it may already be removed. Do not
      //   remove it twice, i.e., do test 'isdead()' together.
      if ((testseg.sh != subsegloop.sh) && !isdead(&testseg)) {
        shellfacedealloc(subsegs, testseg.sh);
      }
      // 'ssbond' bonds the subface and the segment together, and dissloves
      //   the old bond as well.
      ssbond(sface, subsegloop);
    }
    // Set connection between these faces.
    sface = *(face *)(* sfacelist)[0];
    if (sfacelist->len() > 1) {
      for (k = 1; k <= sfacelist->len(); k++) {
        if (k < sfacelist->len()) {
          sface1 = *(face *)(* sfacelist)[k];
        } else {
          sface1 = *(face *)(* sfacelist)[0];    // Form a face loop.
        }
        // Comment: For detecting invalid PLC, here we could check if the
        //   two subfaces "sface" and "sface1" are identical (skipped).
        if (b->verbose > 2) {
          //printf("    Bond subfaces (%d, %d, %d) and (%d, %d, %d).\n",
                 //pointmark(torg), pointmark(tdest), pointmark(sapex(sface)),
                // pointmark(torg), pointmark(tdest), pointmark(sapex(sface1)));
        }
        sbond1(sface, sface1);
        sface = sface1;
      }
    } else {
      // This segment belongs to only on subface.
      sdissolve(sface);
    }
    // Set the unique segment marker into the unified segment.
    setshellmark(subsegloop, segmarker);
    // Increase the marker.
    segmarker++;
    // Clear the working list.
    sfacelist->clear(); 
    subsegloop.sh = shellfacetraverse(subsegs);
  }

  delete [] idx2facelist;
  delete [] facesperverlist;
  delete sfacelist;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// assignsegmentmarkers()    Assign markers given in "in->edgemarkerlist".   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::assignsegmentmarkers()
{
  shellface **segsperverlist;
  face sseg;
  bool isseg;
  int *idx2seglist;
  int end1, end2, tend1, tend2;
  int index, i, j;

  if (b->verbose > 0) {
    //printf("  Assigning segment markers.\n");
  }

  //assert(in->edgemarkerlist != NULL);
  makesegmentmap(idx2seglist, segsperverlist);

  for (i = 0; i < in->numberofedges; i++) {
    end1 = in->edgelist[i * 2];
    end2 = in->edgelist[i * 2 + 1];
    index = end1 - in->firstnumber;
    for (j = idx2seglist[index]; j < idx2seglist[index + 1]; j++) {
      sseg.sh = segsperverlist[j];
      sseg.shver = 0;
      isseg = false;
      tend1 = pointmark(sorg(sseg));
      tend2 = pointmark(sdest(sseg));
      if (tend1 == end1) {
        if (tend2 == end2) isseg = true;
      } else if (tend1 == end2) {
        if (tend2 == end1) isseg = true;
      }
      if (isseg) {
        setshellmark(sseg, in->edgemarkerlist[i]);
        break;
      }
    }
  }

  delete [] idx2seglist;
  delete [] segsperverlist;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// mergefacets()    Merge adjacent facets to be one facet if they are        //
//                  coplanar and have the same boundary marker.              //
//                                                                           //
// Segments between two merged facets will be removed from the mesh.  If all //
// segments around a vertex have been removed, change its vertex type to be  //
// FREESUBVERTEX. Edge flips will be performed to ensure the Delaunayness of //
// the triangulation of merged facets.                                       //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::mergefacets(queue* flipqueue)
{
  face parentsh, neighsh, neineighsh;
  face segloop;
  point eorg, edest;
  REAL ori;
  bool mergeflag, pbcflag;
  int* segspernodelist;
  int fidx1, fidx2;
  int i, j;

  if (b->verbose > 0) {
    //printf("  Merging coplanar facets.\n");
  }
  // Create and initialize 'segspernodelist'.
  segspernodelist = new int[points->items + 1];
  for (i = 0; i < points->items + 1; i++) segspernodelist[i] = 0;

  // Loop the segments, counter the number of segments sharing each vertex.
  subsegs->traversalinit();
  segloop.sh = shellfacetraverse(subsegs);
  while (segloop.sh != (shellface *) NULL) {
    // Increment the number of sharing segments for each endpoint.
    for (i = 0; i < 2; i++) {
      j = pointmark((point) segloop.sh[3 + i]);
      segspernodelist[j]++;
    }
    segloop.sh = shellfacetraverse(subsegs);
  }

  // Loop the segments, find out dead segments.
  subsegs->traversalinit();
  segloop.sh = shellfacetraverse(subsegs);
  while (segloop.sh != (shellface *) NULL) {
    eorg = sorg(segloop);
    edest = sdest(segloop);
    spivot(segloop, parentsh);
    if (parentsh.sh != dummysh) {
      // This segment is not dangling.
      spivot(parentsh, neighsh);
      if (neighsh.sh != dummysh) {
        // This segment belongs to at least two facets.
        spivot(neighsh, neineighsh);
        if ((parentsh.sh != neighsh.sh) && (parentsh.sh == neineighsh.sh)) {
          // Exactly two subfaces at this segment.
          fidx1 = shellmark(parentsh) - 1;
          fidx2 = shellmark(neighsh) - 1;
          pbcflag = false;
          if (checkpbcs) {
            pbcflag = (shellpbcgroup(parentsh) >= 0)
              || (shellpbcgroup(neighsh) >= 0);
          }
          // Possibly merge them if they are not in the same facet.
          if ((fidx1 != fidx2) && !pbcflag) {
            // Test if they are coplanar.
            ori = orient3d(eorg, edest, sapex(parentsh), sapex(neighsh));
            if (ori != 0.0) {
              if (iscoplanar(eorg, edest, sapex(parentsh), sapex(neighsh), ori,
                             b->epsilon)) {
                ori = 0.0; // They are assumed as coplanar.
              }
            }
            if (ori == 0.0) {
              mergeflag = (in->facetmarkerlist == (int *) NULL || 
                in->facetmarkerlist[fidx1] == in->facetmarkerlist[fidx2]);
              if (mergeflag) {
                // This segment becomes dead.
                if (b->verbose > 1) {
                  //printf("  Removing segment (%d, %d).\n", pointmark(eorg),
                         //pointmark(edest));
                }
                ssdissolve(parentsh);
                ssdissolve(neighsh);
                shellfacedealloc(subsegs, segloop.sh);
                j = pointmark(eorg);
                segspernodelist[j]--;
                if (segspernodelist[j] == 0) {
                  setpointtype(eorg, FREESUBVERTEX);
                }
                j = pointmark(edest);
                segspernodelist[j]--;
                if (segspernodelist[j] == 0) {
                  setpointtype(edest, FREESUBVERTEX);
                }
                // Add 'parentsh' to queue checking for flip.
                enqueueflipedge(parentsh, flipqueue);
              }
            }
          }
        }
      } // neighsh.sh != dummysh
    } // parentsh.sh != dummysh
    segloop.sh = shellfacetraverse(subsegs);
  }

  if (!flipqueue->empty()) {
    // Restore the Delaunay property in the facet triangulation.
    lawson(flipqueue);
  }

  delete [] segspernodelist;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// meshsurface()    Create the surface mesh of a PLC.                        //
//                                                                           //
// Let X be the PLC, the surface mesh S of X consists of triangulated facets.//
// S is created mainly in the following steps:                               //
//                                                                           //
// (1) Form the CDT of each facet of X separately (by routine triangulate()).//
// After it is done, the subfaces of each facet are connected to each other, //
// however there is no connection between facets yet.  Notice each facet has //
// its own segments, some of them are duplicated.                            //
//                                                                           //
// (2) Remove the redundant segments created in step (1) (by routine unify-  //
// segment()). The subface ring of each segment is created,  the connection  //
// between facets are established as well.                                   //
//                                                                           //
// The return value indicates the number of segments of X.                   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

long tetgenmesh::meshsurface()
{
  list *ptlist, *conlist;
  queue *flipqueue;
  tetgenio::facet *f;
  tetgenio::polygon *p;
  memorypool *viri;
  point *idx2verlist;
  point tstart, tend, *cons;
  int *worklist;
  int end1, end2;
  int shmark, i, j;

  if (!b->quiet) {
    //printf("Creating surface mesh.\n");
  }

  // Compute a mapping from indices to points.
  makeindex2pointmap(idx2verlist);
  // // Compute a mapping from points to tets for computing abovepoints.
  // makepoint2tetmap();
  // Initialize 'facetabovepointarray'.
  facetabovepointarray = new point[in->numberoffacets + 1];
  for (i = 0; i < in->numberoffacets + 1; i++) {
    facetabovepointarray[i] = (point) NULL;
  }
  if (checkpbcs) {
    // Initialize the global array 'subpbcgrouptable'.
    // createsubpbcgrouptable();
  }

  // Initialize working lists.
  viri = new memorypool(sizeof(shellface *), 1024, POINTER, 0);
  flipqueue = new queue(sizeof(badface));
  ptlist = new list(sizeof(point *), NULL, 256);
  conlist = new list(sizeof(point *) * 2, NULL, 256);
  worklist = new int[points->items + 1];
  for (i = 0; i < points->items + 1; i++) worklist[i] = 0;

  caveshlist = new arraypool(sizeof(face), 10);
  caveshbdlist = new arraypool(sizeof(face), 10);

  // Loop the facet list, triangulate each facet. On finish, all subfaces
  //   are in 'subfaces', all segments are in 'subsegs'. Notice: there're
  //   redundant segments.  Remember: All facet indices count from 1.
  for (shmark = 1; shmark <= in->numberoffacets; shmark++) {    
    // Get a facet F.
    f = &in->facetlist[shmark - 1];

    // Process the duplicated points first, they are marked with type
    //   DUPLICATEDVERTEX by incrflipdelaunay().  Let p and q are dup.
    //   and the index of p is larger than q's, p is substituted by q.
    //   In a STL mesh, duplicated points are implicitly included.
    if ((b->object == tetgenbehavior::STL) || dupverts) {
      // Loop all polygons of this facet.
      for (i = 0; i < f->numberofpolygons; i++) {
        p = &(f->polygonlist[i]);
        // Loop other vertices of this polygon.
        for (j = 0; j < p->numberofvertices; j++) {
          end1 = p->vertexlist[j];
          tstart = idx2verlist[end1 - in->firstnumber];
          if (pointtype(tstart) == DUPLICATEDVERTEX) {
            // Reset the index of vertex-j.
            tend = point2ppt(tstart);
            end2 = pointmark(tend);
            p->vertexlist[j] = end2;
          }
        }
      }
    }

    // Loop polygons of F, get the set V of vertices and S of segments.
    for (i = 0; i < f->numberofpolygons; i++) {
      // Get a polygon.
      p = &(f->polygonlist[i]);
      // Get the first vertex.
      end1 = p->vertexlist[0];
      if ((end1 < in->firstnumber) || 
          (end1 >= in->firstnumber + in->numberofpoints)) {
        if (!b->quiet) {
          //printf("Warning:  Invalid the 1st vertex %d of polygon", end1);
          //printf(" %d in facet %d.\n", i + 1, shmark);
        }
        continue; // Skip this polygon.
      }
      tstart = idx2verlist[end1 - in->firstnumber];
      // Add tstart to V if it haven't been added yet.
      if (worklist[end1] == 0) {
        ptlist->append(&tstart);
        worklist[end1] = 1;
      }
      // Loop other vertices of this polygon.
      for (j = 1; j <= p->numberofvertices; j++) {
        // get a vertex.
        if (j < p->numberofvertices) {
          end2 = p->vertexlist[j];
        } else {
          end2 = p->vertexlist[0];  // Form a loop from last to first.
        }
        if ((end2 < in->firstnumber) ||
            (end2 >= in->firstnumber + in->numberofpoints)) {
          if (!b->quiet) {
            //printf("Warning:  Invalid vertex %d in polygon %d", end2, i + 1);
            //printf(" in facet %d.\n", shmark);
          }
        } else {
          if (end1 != end2) {
            // 'end1' and 'end2' form a segment.
            tend = idx2verlist[end2 - in->firstnumber];
            // Add tstart to V if it haven't been added yet.
            if (worklist[end2] == 0) {
              ptlist->append(&tend);
              worklist[end2] = 1;
            }
            // Save the segment in S (conlist).
            cons = (point *) conlist->append(NULL);
            cons[0] = tstart;
            cons[1] = tend;
            // Set the start for next continuous segment.
            end1 = end2;
            tstart = tend;
          } else {
            // Two identical vertices represent an isolated vertex of F.
            if (p->numberofvertices > 2) {
              // This may be an error in the input, anyway, we can continue
              //   by simply skipping this segment.
              if (!b->quiet) {
                //printf("Warning:  Polygon %d has two identical verts", i + 1);
                //printf(" in facet %d.\n", shmark);
              }
            } 
            // Ignore this vertex.
          } 
        }
        // Is the polygon degenerate (a segment or a vertex)?
        if (p->numberofvertices == 2) break;
      } 
    }
    // Unmark vertices.
    for (i = 0; i < ptlist->len(); i++) {
      tstart = * (point *)(* ptlist)[i];
      end1 = pointmark(tstart);
      //assert(worklist[end1] == 1);
      worklist[end1] = 0;
    }

    // Create a CDT of F.
    triangulate(shmark, b->epsilon * 1e+2, ptlist, conlist, f->numberofholes,
                f->holelist, viri, flipqueue);
    // Clear working lists.
    ptlist->clear();
    conlist->clear();
    viri->restart();
  }

  delete caveshlist;
  delete caveshbdlist;
  caveshlist = NULL;
  caveshbdlist = NULL;

  // Unify segments in 'subsegs', remove redundant segments.  Face links
  //   of segments are also built.
  unifysegments();
  /*if (in->numberofedges > 0) {
    if (in->edgemarkerlist != NULL) {
      assignsegmentmarkers();
    }
  }*/

  // Remember the number of input segments (for output).
  insegments = subsegs->items;

  if (checkpbcs) {
    // Create the global array 'segpbcgrouptable'.
    // createsegpbcgrouptable();
  }

  if (b->object == tetgenbehavior::STL) {
    // Remove redundant vertices (for .stl input mesh).
    jettisonnodes();
  }

  if (!b->nomerge && !b->nobisect && !checkpbcs) {
    // No '-M' switch - merge adjacent facets if they are coplanar.
    mergefacets(flipqueue);
  }

  // Create the point-to-segment map.
  makepoint2segmap();

  delete [] idx2verlist;
  delete [] worklist;
  delete ptlist;
  delete conlist;
  delete flipqueue;
  delete viri;

  return subsegs->items;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// interecursive()    Recursively do intersection test on a set of triangles.//
//                                                                           //
// Recursively split the set 'subfacearray' of subfaces into two sets using  //
// a cut plane parallel to x-, or, y-, or z-axies.  The split criteria are   //
// follows. Assume the cut plane is H, and H+ denotes the left halfspace of  //
// H, and H- denotes the right halfspace of H; and s be a subface:           //
//                                                                           //
//    (1) If all points of s lie at H+, put it into left array;              //
//    (2) If all points of s lie at H-, put it into right array;             //
//    (3) If some points of s lie at H+ and some of lie at H-, or some       //
//        points lie on H, put it into both arraies.                         //
//                                                                           //
// Partitions by x-axis if axis == '0'; by y-axis if axis == '1'; by z-axis  //
// if axis == '2'. If current cut plane is parallel to the x-axis, the next  //
// one will be parallel to y-axis, and the next one after the next is z-axis,//
// and then alternately return back to x-axis.                               //
//                                                                           //
// Stop splitting when the number of triangles of the input array is not     //
// decreased anymore. Do tests on the current set.                           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::
interecursive(shellface** subfacearray, int arraysize, int axis, REAL bxmin,
              REAL bxmax, REAL bymin, REAL bymax, REAL bzmin, REAL bzmax,
              int* internum)
{
  shellface **leftarray, **rightarray;
  face sface1, sface2;
  point p1, p2, p3;
  point p4, p5, p6;
  enum interresult intersect;
  REAL split;
  bool toleft, toright;
  int leftsize, rightsize;
  int i, j;

  if (b->verbose > 1) {
    //printf("  Recur %d faces. Bbox (%g, %g, %g),(%g, %g, %g). %s-axis\n",
           //arraysize, bxmin, bymin, bzmin, bxmax, bymax, bzmax,
          // axis == 0 ? "x" : (axis == 1 ? "y" : "z"));
  }
    
  leftarray = new shellface*[arraysize];
  if (leftarray == NULL) {
    terminatetetgen(1);
  }
  rightarray = new shellface*[arraysize];
  if (rightarray == NULL) {
    terminatetetgen(1);
  }
  leftsize = rightsize = 0;

  if (axis == 0) {
    // Split along x-axis.
    split = 0.5 * (bxmin + bxmax);
  } else if (axis == 1) {
    // Split along y-axis.
    split = 0.5 * (bymin + bymax);
  } else {
    // Split along z-axis.
    split = 0.5 * (bzmin + bzmax);
  }

  for (i = 0; i < arraysize; i++) {
    sface1.sh = subfacearray[i];
    p1 = (point) sface1.sh[3];
    p2 = (point) sface1.sh[4];
    p3 = (point) sface1.sh[5];
    toleft = toright = false;
    if (p1[axis] < split) {
      toleft = true;
      if (p2[axis] >= split || p3[axis] >= split) {
        toright = true;
      } 
    } else if (p1[axis] > split) {
      toright = true;
      if (p2[axis] <= split || p3[axis] <= split) {
        toleft = true;
      } 
    } else {
      // p1[axis] == split;
      toleft = true;
      toright = true;
    }
    // At least one is true;
#ifdef SELF_CHECK
    //assert(!(toleft == false && toright == false));
#endif
    if (toleft) {
      leftarray[leftsize] = sface1.sh;
      leftsize++;
    }
    if (toright) {
      rightarray[rightsize] = sface1.sh;
      rightsize++;
    }
  }

  if (leftsize < arraysize && rightsize < arraysize) {
    // Continue to partition the input set. Now 'subfacearray' has been
    //   split into two sets, it's memory can be freed. 'leftarray' and
    //   'rightarray' will be freed in the next recursive (after they're
    //   partitioned again or performing tests).
    delete [] subfacearray;
    // Continue to split these two sets.
    if (axis == 0) {
      interecursive(leftarray, leftsize, 1, bxmin, split, bymin, bymax,
                    bzmin, bzmax, internum);
      interecursive(rightarray, rightsize, 1, split, bxmax, bymin, bymax,
                    bzmin, bzmax, internum);
    } else if (axis == 1) {
      interecursive(leftarray, leftsize, 2, bxmin, bxmax, bymin, split,
                    bzmin, bzmax, internum);
      interecursive(rightarray, rightsize, 2, bxmin, bxmax, split, bymax,
                    bzmin, bzmax, internum);
    } else {
      interecursive(leftarray, leftsize, 0, bxmin, bxmax, bymin, bymax,
                    bzmin, split, internum);
      interecursive(rightarray, rightsize, 0, bxmin, bxmax, bymin, bymax,
                    split, bzmax, internum);
    }
  } else {
    if (b->verbose > 1) {
      //printf("  Checking intersecting faces.\n");
    }
    // Perform a brute-force compare on the set.
    for (i = 0; i < arraysize; i++) {
      sface1.sh = subfacearray[i];
      p1 = (point) sface1.sh[3];
      p2 = (point) sface1.sh[4];
      p3 = (point) sface1.sh[5];
      for (j = i + 1; j < arraysize; j++) {
        sface2.sh = subfacearray[j];
        p4 = (point) sface2.sh[3];
        p5 = (point) sface2.sh[4];
        p6 = (point) sface2.sh[5];
        intersect = tri_tri_inter(p1, p2, p3, p4, p5, p6);
        if (intersect == INTERSECT || intersect == SHAREFACE) {
          if (!b->quiet) {
            if (intersect == INTERSECT) {
              //printf("  Facet #%d intersects facet #%d at triangles:\n",
                     //shellmark(sface1), shellmark(sface2));
              //printf("    (%4d, %4d, %4d) and (%4d, %4d, %4d)\n",
                     //pointmark(p1), pointmark(p2), pointmark(p3),
                     //pointmark(p4), pointmark(p5), pointmark(p6));
            } else {
              //printf("  Facet #%d duplicates facet #%d at triangle:\n",
                     //shellmark(sface1), shellmark(sface2));
              //printf("    (%4d, %4d, %4d)\n", pointmark(p1), pointmark(p2),
                     //pointmark(p3));
            }
          }
          // Increase the number of intersecting pairs.
          (*internum)++; 
          // Infect these two faces (although they may already be infected).
          sinfect(sface1);
          sinfect(sface2);
        }
      }
    }
    // Don't forget to free all three arrays. No further partition.
    delete [] leftarray;
    delete [] rightarray;  
    delete [] subfacearray;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// detectinterfaces()    Detect intersecting triangles.                      //
//                                                                           //
// Given a set of triangles,  find the pairs of intersecting triangles from  //
// them.  Here the set of triangles is in 'subfaces' which is a surface mesh //
// of a PLC (.poly or .smesh).                                               //
//                                                                           //
// To detect whether two triangles are intersecting is done by the routine   //
// 'tri_tri_inter()'.  The algorithm for the test is very simple and stable. //
// It is based on geometric orientation test which uses exact arithmetics.   //
//                                                                           //
// Use divide-and-conquer algorithm for reducing the number of intersection  //
// tests.  Start from the bounding box of the input point set, recursively   //
// partition the box into smaller boxes, until the number of triangles in a  //
// box is not decreased anymore. Then perform triangle-triangle tests on the //
// remaining set of triangles.  The memory allocated in the input set is     //
// freed immediately after it has been partitioned into two arrays.  So it   //
// can be re-used for the consequent partitions.                             //
//                                                                           //
// On return, the pool 'subfaces' will be cleared, and only the intersecting //
// triangles remain for output (to a .face file).                            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::detectinterfaces()
{
  shellface **subfacearray;
  face shloop;
  int internum;
  int i;

  if (!b->quiet) {
    //printf("Detecting intersecting facets.\n");
  }

  // Construct a map from indices to subfaces;
  subfacearray = new shellface*[subfaces->items];
  subfaces->traversalinit();
  shloop.sh = shellfacetraverse(subfaces);
  i = 0;
  while (shloop.sh != (shellface *) NULL) {
    subfacearray[i] = shloop.sh;
    shloop.sh = shellfacetraverse(subfaces);
    i++;
  }

  internum = 0;
  // Recursively split the set of triangles into two sets using a cut plane
  //   parallel to x-, or, y-, or z-axies.  Stop splitting when the number
  //   of subfaces is not decreasing anymore. Do tests on the current set.
  interecursive(subfacearray, subfaces->items, 0, xmin, xmax, ymin, ymax,
                zmin, zmax, &internum);

  if (!b->quiet) {
    if (internum > 0) {
      //printf("\n!! Found %d pairs of faces are intersecting.\n\n", internum);
    } else {
      //printf("\nNo faces are intersecting.\n\n");
    }
  }

  if (internum > 0) {
    // Traverse all subfaces, deallocate those have not been infected (they
    //   are not intersecting faces). Uninfect those have been infected.
    //   After this loop, only intersecting faces remain.
    subfaces->traversalinit();
    shloop.sh = shellfacetraverse(subfaces);
    while (shloop.sh != (shellface *) NULL) {
      if (sinfected(shloop)) {
        suninfect(shloop);
      } else {
        shellfacedealloc(subfaces, shloop.sh);
      }
      shloop.sh = shellfacetraverse(subfaces);
    }
  } else {
    // Deallocate all subfaces.
    subfaces->restart();
  }
}

////                                                                       ////
////                                                                       ////
//// surface_cxx //////////////////////////////////////////////////////////////

//// constrained_cxx //////////////////////////////////////////////////////////
////                                                                       ////
////                                                                       ////

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// markacutevertices()    Mark acute vertices.                               //
//                                                                           //
// A vertex v is called acute if there are two segments sharing at v forming //
// an acute angle (i.e. smaller than 90 degree).                             //
//                                                                           //
// This routine finds all acute vertices in the PLC and marks them as point- //
// type ACUTEVERTEX. The other vertices of segments which are non-acute will //
// be marked as NACUTEVERTEX.  Vertices which are not endpoints of segments  //
// (such as DUPLICATEDVERTEX, UNUSEDVERTEX, etc) are not infected.           //
//                                                                           //
// NOTE: This routine should be called before Steiner points are introduced. //
// That is, no point has type like FREESEGVERTEX, etc.                       //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::markacutevertices(REAL acuteangle)
{
  shellface **segsperverlist;
  face segloop, nextseg;
  point pointloop, edest, eapex;
  REAL cosbound, anglearc;
  REAL v1[3], v2[3], L, D;
  bool isacute;
  int *idx2seglist;
  int acutecount;
  int idx, i, j, k;

  if (b->verbose > 0) {
    //printf("  Marking acute vertices.\n");
  }

  anglearc = acuteangle * PI / 180.0;
  cosbound = cos(anglearc);
  acutecount = 0;
  // Constructing a map from vertex to segments.
  makesegmentmap(idx2seglist, segsperverlist);

  // Loop over the set of vertices.
  points->traversalinit();
  pointloop = pointtraverse();
  while (pointloop != (point) NULL) {
    idx = pointmark(pointloop) - in->firstnumber;
    // Only do test if p is an endpoint of some segments.
    if (idx2seglist[idx + 1] > idx2seglist[idx]) {
      // Init p to be non-acute.
      setpointtype(pointloop, NACUTEVERTEX);
      isacute = false;
      // Loop through all segments sharing at p.
      for (i = idx2seglist[idx]; i < idx2seglist[idx + 1] && !isacute; i++) {
        segloop.sh = segsperverlist[i];
        // segloop.shver = 0;
        if (sorg(segloop) != pointloop) sesymself(segloop);
        edest = sdest(segloop);
        for (j = i + 1; j < idx2seglist[idx + 1] && !isacute; j++) {
          nextseg.sh = segsperverlist[j];
          // nextseg.shver = 0;
          if (sorg(nextseg) != pointloop) sesymself(nextseg);
          eapex = sdest(nextseg);
          // Check the angle formed by segs (p, edest) and (p, eapex).
          for (k = 0; k < 3; k++) {
            v1[k] = edest[k] - pointloop[k];
            v2[k] = eapex[k] - pointloop[k];
          }
          L = sqrt(v1[0] * v1[0] + v1[1] * v1[1] + v1[2] * v1[2]);
          for (k = 0; k < 3; k++) v1[k] /= L;
          L = sqrt(v2[0] * v2[0] + v2[1] * v2[1] + v2[2] * v2[2]);
          for (k = 0; k < 3; k++) v2[k] /= L;
          D = v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2];
          // Is D acute?
          isacute = (D >= cosbound);
        }
      }
      if (isacute) {
        // Mark p to be acute.
        setpointtype(pointloop, ACUTEVERTEX);
        acutecount++;
      }
    }
    pointloop = pointtraverse();
  }

  delete [] idx2seglist;
  delete [] segsperverlist;

  if ((b->verbose > 0) && (acutecount > 0)) {
    //printf("  %d acute vertices.\n", acutecount);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// finddirection()    Find the first tetrahedron on the path from one point  //
//                    to another.                                            //
//                                                                           //
// Find the tetrahedron that intersects a line segment L (from the origin of //
// 'searchtet' to the point 'tend'), and returns the result in 'searchtet'.  //
// The origin of 'searchtet' does not change, even though the tetrahedron    //
// returned may differ from the one passed in.  This routine is used to find //
// the direction to move in to get from one point to another.                //
//                                                                           //
// The return value notes the location of the line segment L with respect to //
// 'searchtet':                                                              //
//   - Returns RIGHTCOLLINEAR indicates L is collinear with the line segment //
//     from the origin to the destination of 'searchtet'.                    //
//   - Returns LEFTCOLLINEAR indicates L is collinear with the line segment  //
//     from the origin to the apex of 'searchtet'.                           //
//   - Returns TOPCOLLINEAR indicates L is collinear with the line segment   //
//     from the origin to the opposite of 'searchtet'.                       //
//   - Returns ACROSSEDGE indicates L intersects with the line segment from  //
//     the destination to the apex of 'searchtet'.                           //
//   - Returns ACROSSFACE indicates L intersects with the face opposite to   //
//     the origin of 'searchtet'.                                            //
//   - Returns BELOWHULL indicates L crosses outside the mesh domain. This   //
//     can only happen when the domain is non-convex.                        //
//                                                                           //
// NOTE: This routine only works correctly when the mesh is exactly Delaunay.//
//                                                                           //
// If 'maxtetnumber' > 0, stop the searching process if the number of passed //
// tets is larger than it. Return BELOWHULL.                                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

enum tetgenmesh::finddirectionresult tetgenmesh::
finddirection(triface *searchtet, point tend, long maxtetnumber)
{
  triface neightet;
  point tstart, tdest, tapex, toppo;
  REAL ori1, ori2, ori3;
  long tetnumber;

  tstart = org(*searchtet);
#ifdef SELF_CHECK
  //assert(tstart != tend);
#endif
  adjustedgering(*searchtet, CCW);
  if (tstart != org(*searchtet)) {
    enextself(*searchtet); // For keeping the same origin.
  }
  tdest = dest(*searchtet);
  if (tdest == tend) {
    return RIGHTCOLLINEAR;
  }
  tapex = apex(*searchtet); 
  if (tapex == tend) {
    return LEFTCOLLINEAR;
  } 

  ori1 = orient3d(tstart, tdest, tapex, tend);
  if (ori1 > 0.0) {
    // 'tend' is below the face, get the neighbor of this side.
    sym(*searchtet, neightet);
    if (neightet.tet != dummytet) {
      findorg(&neightet, tstart); 
      adjustedgering(neightet, CCW);
      if (org(neightet) != tstart) {
        enextself(neightet); // keep the same origin.
      }
      // Set the changed configuratiuon.
      *searchtet = neightet; 
      ori1 = -1.0; 
      tdest = dest(*searchtet);
      tapex = apex(*searchtet);
    } else {
      // A hull face. Only possible for a nonconvex mesh.
#ifdef SELF_CHECK
      //assert(nonconvex);
#endif
      return BELOWHULL; 
    }
  }

  // Repeatedly change the 'searchtet', remain 'tstart' be its origin, until
  //   find a tetrahedron contains 'tend' or is crossed by the line segment
  //   from 'tstart' to 'tend'.
  tetnumber = 0l;
  while ((maxtetnumber > 0) && (tetnumber <= maxtetnumber)) {
    tetnumber++;
    toppo = oppo(*searchtet);
    if (toppo == tend) {
      return TOPCOLLINEAR;
    }
    ori2 = orient3d(tstart, toppo, tdest, tend);
    if (ori2 > 0.0) {
      // 'tend' is below the face, get the neighbor at this side.
      fnext(*searchtet, neightet);
      symself(neightet);
      if (neightet.tet != dummytet) {
        findorg(&neightet, tstart); 
        adjustedgering(neightet, CCW);
        if (org(neightet) != tstart) {
          enextself(neightet); // keep the same origin.
        }
        // Set the changed configuration.
        *searchtet = neightet; 
        ori1 = -1.0; 
        tdest = dest(*searchtet);
        tapex = apex(*searchtet);
        // Continue the search from the changed 'searchtet'.
        continue;
      } else {
        // A hull face. Only possible for a nonconvex mesh.
#ifdef SELF_CHECK
        //assert(nonconvex);
#endif
        return BELOWHULL; 
      }
    }
    ori3 = orient3d(tapex, toppo, tstart, tend);
    if (ori3 > 0.0) {
      // 'tend' is below the face, get the neighbor at this side.
      enext2fnext(*searchtet, neightet);
      symself(neightet);
      if (neightet.tet != dummytet) {
        findorg(&neightet, tstart); 
        adjustedgering(neightet, CCW);
        if (org(neightet) != tstart) {
          enextself(neightet); // keep the same origin.
        }
        // Set the changed configuration.
        *searchtet = neightet; 
        ori1 = -1.0; 
        tdest = dest(*searchtet);
        tapex = apex(*searchtet);
        // Continue the search from the changed 'searchtet'.
        continue;
      } else {
        // A hull face. Only possible for a nonconvex mesh.
#ifdef SELF_CHECK
        //assert(nonconvex);
#endif
        return BELOWHULL; 
      }
    }
    // Now 'ori1', 'ori2' and 'ori3' are possible be 0.0 or all < 0.0;
    if (ori1 < 0.0) {
      // Possible cases are: ACROSSFACE, ACROSSEDGE, TOPCOLLINEAR.
      if (ori2 < 0.0) {
        if (ori3 < 0.0) {
          return ACROSSFACE;
        } else { // ori3 == 0.0;
          // Cross edge (apex, oppo)
          enext2fnextself(*searchtet);
          esymself(*searchtet); // org(*searchtet) == tstart;
          return ACROSSEDGE;
        }
      } else { // ori2 == 0.0; 
        if (ori3 < 0.0) {
          // Cross edge (dest, oppo)
          fnextself(*searchtet);
          esymself(*searchtet);
          enextself(*searchtet); // org(*searchtet) == tstart;
          return ACROSSEDGE;
        } else { // ori3 == 0.0;
          // Collinear with edge (org, oppo)
          return TOPCOLLINEAR;
        }
      }
    } else { // ori1 == 0.0;
      // Possible cases are: RIGHTCOLLINEAR, LEFTCOLLINEAR, ACROSSEDGE.
      if (ori2 < 0.0) {
        if (ori3 < 0.0) {
          // Cross edge (tdest, tapex)
          return ACROSSEDGE;
        } else { // ori3 == 0.0
          // Collinear with edge (torg, tapex)
          return LEFTCOLLINEAR;
        }
      } else { // ori2 == 0.0;
#ifdef SELF_CHECK
        //assert(ori3 != 0.0);
#endif
        // Collinear with edge (torg, tdest)
        return RIGHTCOLLINEAR;
      }
    }
  }
  // Loop breakout. It may happen when the mesh is non-Delaunay.
  return BELOWHULL;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// finddirection()    Find the tet on the path from one point to another.    //
//                                                                           //
// The path starts from 'searchtet''s origin and ends at 'endpt'. On finish, //
// 'searchtet' contains a tet on the path, its origin does not change.       //
//                                                                           //
// The return value indicates one of the following cases (let 'searchtet' be //
// abcd, a is the origin of the path):                                       //
//   - ACROSSVERT, edge ab is collinear with the path;                       //
//   - ACROSSEDGE, edge bc intersects with the path;                         //
//   - ACROSSFACE, face bcd intersects with the path.                        //
//                                                                           //
// WARNING: This routine is designed for convex triangulations, and will not //
// generally work after the holes and concavities have been carved.          //
//   - BELOWHULL2, the mesh is non-convex and the searching for the path has //
//                 got stucked at a non-convex boundary face.                //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

enum tetgenmesh::interresult tetgenmesh::finddirection2(triface* searchtet, 
  point endpt)
{
  triface neightet;
  point pa, pb, pc, pd, pn;
  enum {HMOVE, RMOVE, LMOVE} nextmove;
  enum {HCOPLANE, RCOPLANE, LCOPLANE, NCOPLANE} cop;
  REAL hori, rori, lori;
  REAL dmin, dist;

  //assert((searchtet->tet != NULL) && (searchtet->tet != dummytet));

  // The origin is fixed.
  pa = org(*searchtet);
  if (searchtet->ver & 01) {
    // Switch to the 0th edge ring.
    esymself(*searchtet);
    enextself(*searchtet);
  }
  pb = dest(*searchtet);
  if (pb == endpt) {
    // pa->pb is the search edge.
    return INTERVERT;
  }
  pc = apex(*searchtet);
  if (pc == endpt) {
    // pa->pc is the search edge.
    enext2self(*searchtet);
    esymself(*searchtet);
    return INTERVERT;
  }

  // Walk through tets at pa until the right one is found.
  while (1) {

    pd = oppo(*searchtet);

    if (b->verbose > 2) {
      //printf("      From tet (%d, %d, %d, %d) to %d.\n", pointmark(pa),
        //pointmark(pb), pointmark(pc), pointmark(pd), pointmark(endpt));
    }

    // Check whether the opposite vertex is 'endpt'.
    if (pd == endpt) {
      // pa->pd is the search edge.
      fnextself(*searchtet);
      enext2self(*searchtet);
      esymself(*searchtet);
      return INTERVERT;
    }

    // Now assume that the base face abc coincides with the horizon plane,
    //   and d lies above the horizon.  The search point 'endpt' may lie
    //   above or below the horizon.  We test the orientations of 'endpt'
    //   with respect to three planes: abc (horizon), bad (right plane),
    //   and acd (left plane). 
    hori = orient3d(pa, pb, pc, endpt);
    rori = orient3d(pb, pa, pd, endpt);
    lori = orient3d(pa, pc, pd, endpt);
    orient3dcount += 3;

    // Now decide the tet to move.  It is possible there are more than one
    //   tet are viable moves. Use the opposite points of thier neighbors
    //   to discriminate, i.e., we choose the tet whose opposite point has
    //   the shortest distance to 'endpt'.
    if (hori > 0) {
      if (rori > 0) {
        if (lori > 0) {
          // Any of the three neighbors is a viable move.
          nextmove = HMOVE;
          sym(*searchtet, neightet);
          if (neightet.tet != dummytet) {
            pn = oppo(neightet);
            dmin = NORM2(endpt[0] - pn[0], endpt[1] - pn[1], endpt[2] - pn[2]);
          } else {
            dmin = NORM2(xmax - xmin, ymax - ymin, zmax - zmin);
          }
          fnext(*searchtet, neightet);
          symself(neightet);
          if (neightet.tet != dummytet) {
            pn = oppo(neightet);
            dist = NORM2(endpt[0] - pn[0], endpt[1] - pn[1], endpt[2] - pn[2]);
          } else {
            dist = dmin;
          }
          if (dist < dmin) {
            nextmove = RMOVE;
            dmin = dist;
          }
          enext2fnext(*searchtet, neightet);
          symself(neightet);
          if (neightet.tet != dummytet) {
            pn = oppo(neightet);
            dist = NORM2(endpt[0] - pn[0], endpt[1] - pn[1], endpt[2] - pn[2]);
          } else {
            dist = dmin;
          }
          if (dist < dmin) {
            nextmove = LMOVE;
            dmin = dist;
          }
        } else {
          // Two tets, below horizon and below right, are viable.
          nextmove = HMOVE;
          sym(*searchtet, neightet);
          if (neightet.tet != dummytet) {
            pn = oppo(neightet);
            dmin = NORM2(endpt[0] - pn[0], endpt[1] - pn[1], endpt[2] - pn[2]);
          } else {
            dmin = NORM2(xmax - xmin, ymax - ymin, zmax - zmin);
          }
          fnext(*searchtet, neightet);
          symself(neightet);
          if (neightet.tet != dummytet) {
            pn = oppo(neightet);
            dist = NORM2(endpt[0] - pn[0], endpt[1] - pn[1], endpt[2] - pn[2]);
          } else {
            dist = dmin;
          }
          if (dist < dmin) {
            nextmove = RMOVE;
            dmin = dist;
          }
        }
      } else {
        if (lori > 0) {
          // Two tets, below horizon and below left, are viable.
          nextmove = HMOVE;
          sym(*searchtet, neightet);
          if (neightet.tet != dummytet) {
            pn = oppo(neightet);
            dmin = NORM2(endpt[0] - pn[0], endpt[1] - pn[1], endpt[2] - pn[2]);
          } else {
            dmin = NORM2(xmax - xmin, ymax - ymin, zmax - zmin);
          }
          enext2fnext(*searchtet, neightet);
          symself(neightet);
          if (neightet.tet != dummytet) {
            pn = oppo(neightet);
            dist = NORM2(endpt[0] - pn[0], endpt[1] - pn[1], endpt[2] - pn[2]);
          } else {
            dist = dmin;
          }
          if (dist < dmin) {
            nextmove = LMOVE;
            dmin = dist;
          }
        } else {
          // The tet below horizon is chosen.
          nextmove = HMOVE;
        }
      }
    } else {
      if (rori > 0) {
        if (lori > 0) {
          // Two tets, below right and below left, are viable.
          nextmove = RMOVE;
          fnext(*searchtet, neightet);
          symself(neightet);
          if (neightet.tet != dummytet) {
            pn = oppo(neightet);
            dmin = NORM2(endpt[0] - pn[0], endpt[1] - pn[1], endpt[2] - pn[2]);
          } else {
            dmin = NORM2(xmax - xmin, ymax - ymin, zmax - zmin);
          }
          enext2fnext(*searchtet, neightet);
          symself(neightet);
          if (neightet.tet != dummytet) {
            pn = oppo(neightet);
            dist = NORM2(endpt[0] - pn[0], endpt[1] - pn[1], endpt[2] - pn[2]);
          } else {
            dist = dmin;
          }
          if (dist < dmin) {
            nextmove = LMOVE;
            dmin = dist;
          }
        } else {
          // The tet below right is chosen.
          nextmove = RMOVE;
        }
      } else {
        if (lori > 0) {
          // The tet below left is chosen.
          nextmove = LMOVE;
        } else {
          // 'endpt' lies either on the plane(s) or across face bcd.
          if (hori == 0) {
            if (rori == 0) {
              // pa->'endpt' is COLLINEAR with pa->pb.
              return INTERVERT;
            }
            if (lori == 0) {
              // pa->'endpt' is COLLINEAR with pa->pc.
              enext2self(*searchtet);
              esymself(*searchtet);
              return INTERVERT;
            }
            // pa->'endpt' crosses the edge pb->pc.
            // enextself(*searchtet);
            // return INTEREDGE;
            cop = HCOPLANE;
            break;
          }
          if (rori == 0) {
            if (lori == 0) {
              // pa->'endpt' is COLLINEAR with pa->pd.
              fnextself(*searchtet); // face abd.
              enext2self(*searchtet);
              esymself(*searchtet);
              return INTERVERT;
            }
            // pa->'endpt' crosses the edge pb->pd.
            // fnextself(*searchtet); // face abd.
            // enextself(*searchtet);
            // return INTEREDGE;
            cop = RCOPLANE;
            break;
          }
          if (lori == 0) {
            // pa->'endpt' crosses the edge pc->pd.
            // enext2fnextself(*searchtet);  // face cad
            // enext2self(*searchtet);
            // return INTEREDGE;
            cop = LCOPLANE;
            break;
          }
          // pa->'endpt' crosses the face bcd.
          // enextfnextself(*searchtet);
          // return INTERFACE;
          cop = NCOPLANE;
          break;
        }
      }
    }

    // Move to the next tet, fix pa as its origin.
    if (nextmove == RMOVE) {
      tfnextself(*searchtet);
    } else if (nextmove == LMOVE) {
      enext2self(*searchtet);
      tfnextself(*searchtet);
      enextself(*searchtet);
    } else { // HMOVE
      symedgeself(*searchtet);
      enextself(*searchtet);
    }
    // Assume convex case, we should not move to outside.
    if (searchtet->tet == dummytet) {
      // This should only happen when the domain is non-convex.
      return BELOWHULL2;
    }
    //assert(org(*searchtet) == pa); // SELF_CHECK
    pb = dest(*searchtet);
    pc = apex(*searchtet);

  } // while (1)

  // Either case INTEREDGE or INTERFACE.
  /*if (b->epsilon > 0) {
    // Use tolerance to re-evaluate the orientations.
    if (cop != HCOPLANE) {
      if (iscoplanar(pa, pb, pc, endpt, hori)) hori = 0;
    }
    if (cop != RCOPLANE) {
      if (iscoplanar(pb, pa, pd, endpt, rori)) rori = 0;
    }
    if (cop != LCOPLANE) {
      if (iscoplanar(pa, pc, pd, endpt, lori)) lori = 0;
    }
    // It is not possible that all orientations are zero.
    //assert(!((hori == 0) && (rori == 0) && (lori == 0))); // SELF_CHECK
  }*/
  
  // Now decide the degenerate cases.
  if (hori == 0) {
    if (rori == 0) {
      // pa->'endpt' is COLLINEAR with pa->pb.
      return INTERVERT;
    }
    if (lori == 0) {
      // pa->'endpt' is COLLINEAR with pa->pc.
      enext2self(*searchtet);
      esymself(*searchtet);
      return INTERVERT;
    }
    // pa->'endpt' crosses the edge pb->pc.
    return INTEREDGE;
  }
  if (rori == 0) {
    if (lori == 0) {
      // pa->'endpt' is COLLINEAR with pa->pd.
      fnextself(*searchtet); // face abd.
      enext2self(*searchtet);
      esymself(*searchtet);
      return INTERVERT;
    }
    // pa->'endpt' crosses the edge pb->pd.
    fnextself(*searchtet); // face abd.
    esymself(*searchtet);
    enextself(*searchtet);
    return INTEREDGE;
  }
  if (lori == 0) {
    // pa->'endpt' crosses the edge pc->pd.
    enext2fnextself(*searchtet);  // face cad
    esymself(*searchtet);
    return INTEREDGE;
  }
  // pa->'endpt' crosses the face bcd.
  return INTERFACE;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// finddirection3()    Used when finddirection2() returns BELOWHULL2.        //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

enum tetgenmesh::interresult tetgenmesh::finddirection3(triface* searchtet, 
  point endpt)
{
  arraypool *startetlist;
  triface *parytet, oppoface, neightet;
  point startpt, pa, pb, pc;
  enum interresult dir;
  int types[2], poss[4];
  int pos, i, j;

  startetlist = new arraypool(sizeof(triface), 8);
  startpt = org(*searchtet);
  infect(*searchtet);
  startetlist->newindex((void **) &parytet);
  *parytet = *searchtet;

  if (b->verbose > 1) {
    //printf("      Search path (%d, %d) under non-convexity.\n", 
      //pointmark(startpt), pointmark(endpt));
  }

  for (i = 0; i < (int) startetlist->objects; i++) {
    parytet = (triface *) fastlookup(startetlist, i);
    *searchtet = *parytet;
    // //assert(org(*searchtet) == startpt);
    adjustedgering(*searchtet, CCW);
    if (org(*searchtet) != startpt) {
      enextself(*searchtet);
      //assert(org(*searchtet) == startpt);
    }
    // Go to the opposite face of startpt.
    enextfnext(*searchtet, oppoface);
    esymself(oppoface);
    pa = org(oppoface);
    pb = dest(oppoface);
    pc = apex(oppoface);
    // Check if face [a, b, c] intersects the searching path.
    if (tri_edge_test(pa, pb, pc, startpt, endpt, NULL, 1, types, poss)) {
      // They intersect. Get the type of intersection.
      dir = (enum interresult) types[0];
      pos = poss[0];
      break;
    } else {
      dir = DISJOINT;
    }
    // Get the neighbor tets.
    for (j = 0; j < 3; j++) {
      if (j == 0) {
        symedge(*searchtet, neightet);
      } else if (j == 1) {
        fnext(*searchtet, neightet);
        symedgeself(neightet);
      } else {
        enext2fnext(*searchtet, neightet);
        symedgeself(neightet);
      }
      if (neightet.tet != dummytet) {
        if (!infected(neightet)) {
          if (org(neightet) != startpt) esymself(neightet);
          infect(neightet);
          startetlist->newindex((void **) &parytet);
          *parytet = neightet;
        }
      }
    }
  }

  for (i = 0; i < (int) startetlist->objects; i++) {
    parytet = (triface *) fastlookup(startetlist, i);
    uninfect(*parytet);
  }
  delete startetlist;

  if (dir == INTERVERT) {
    // This path passing a vertex of the face [a, b, c].
    if (pos == 0) {
      // The path acrosses pa.
      enext2self(*searchtet);
      esymself(*searchtet); 
    } else if (pos == 1) {
      // The path acrosses pa.
    } else { // pos == 2
      // The path acrosses pc.
      fnextself(*searchtet);
      enext2self(*searchtet);
      esymself(*searchtet);
    }
    return INTERVERT;
  }
  if (dir == INTEREDGE) {
    // This path passing an edge of the face [a, b, c].
    if (pos == 0) {
      // The path intersects [pa, pb].
    } else if (pos == 1) {
      // The path intersects [pb, pc].
      fnextself(*searchtet);
      enext2self(*searchtet);
      esymself(*searchtet);
    } else { // pos == 2
      // The path intersects [pc, pa].
      enext2fnextself(*searchtet);
      esymself(*searchtet);
    }
    return INTEREDGE;
  }
  if (dir == INTERFACE) {
    return INTERFACE;
  }

  // The path does not intersect any tet at pa.
  return BELOWHULL2;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// scoutsegment()    Look for a given segment in the tetrahedralization T.   //
//                                                                           //
// Search an edge in the tetrahedralization that matches the given segmment. //
// If such an edge exists, the segment is 'locked' at the edge. 'searchtet'  //
// returns this (constrained) edge. Otherwise, the segment is missing.       //
//                                                                           //
// The returned value indicates one of the following cases:                  //
//   - SHAREEDGE, the segment exists and is inserted in T;                   //
//   - INTERVERT, the segment intersects a vertex ('refpt').                 //
//   - INTEREDGE, the segment intersects an edge (in 'searchtet').           //
//   - INTERFACE, the segment crosses a face (in 'searchtet').               //
//                                                                           //
// If the returned value is INTEREDGE or INTERFACE, i.e., the segment is     //
// missing, 'refpt' returns the reference point for splitting thus segment,  //
// 'searchtet' returns a tet containing the 'refpt'.                         //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

enum tetgenmesh::interresult tetgenmesh::scoutsegment2(face* sseg,
  triface* searchtet, point* refpt)
{
  triface neightet, reftet;
  face splitsh, checkseg;
  point startpt, endpt;
  point pa, pb, pc, pd;
  enum interresult dir;
  REAL angmax, ang;
  long facecount;
  int hitbdry;
  int types[2], poss[4];
  int pos, i;

  // Is 'searchtet' a valid handle?
  if ((searchtet->tet == NULL) || (searchtet->tet == dummytet)) {
    startpt = sorg(*sseg);
    point2tetorg(startpt, *searchtet);
  } else {
    startpt = sorg(*sseg);
  }
  //assert(org(*searchtet) == startpt); // SELF_CHECK
  endpt = sdest(*sseg);

  if (b->verbose > 1) {
    //printf("    Scout seg (%d, %d).\n", pointmark(startpt), pointmark(endpt));
  }

  dir = finddirection2(searchtet, endpt);

  if (dir == INTERVERT) {
    pd = dest(*searchtet);
    if (pd == endpt) {
      // Found! Insert the segment.
      tsspivot1(*searchtet, checkseg);  // SELF_CHECK
      if (checkseg.sh == dummysh) {
        neightet = *searchtet;
        hitbdry = 0;
        do {
          tssbond1(neightet, *sseg);
          tfnextself(neightet);
          if (neightet.tet == dummytet) {
            hitbdry++;
            if (hitbdry == 2) break;
            esym(*searchtet, neightet);
            tfnextself(neightet);
            if (neightet.tet == dummytet) break;
          }
        } while (neightet.tet != searchtet->tet);
      } else {
        // Collision! This can happy during facet recovery.
        // See fig/dump-cavity-case19, -case20.
        //assert(checkseg.sh == sseg->sh); // SELF_CHECK
      }
      // The job is done. 
      return SHAREEDGE;
    } else {
      // A point is on the path.
      *refpt = pd;
      return INTERVERT;
    }
  }

  if (b->verbose > 1) {
    //printf("    Scout ref point of seg (%d, %d).\n", pointmark(startpt),
      //pointmark(endpt));
  }
  facecount = across_face_count;

  enextfnextself(*searchtet); // Go to the opposite face.
  symedgeself(*searchtet); // Enter the adjacent tet.

  pa = org(*searchtet);
  angmax = interiorangle(pa, startpt, endpt, NULL);
  *refpt = pa;
  pb = dest(*searchtet);
  ang = interiorangle(pb, startpt, endpt, NULL);
  if (ang > angmax) {
    angmax = ang;
    *refpt = pb;
  }

  // Check whether two segments are intersecting.
  if (dir == INTEREDGE) {
    tsspivot1(*searchtet, checkseg);
    if (checkseg.sh != dummysh) {
      //printf("Error:  Invalid PLC. Two segments intersect.\n");
      startpt = getsubsegfarorg(sseg);
      endpt = getsubsegfardest(sseg);
      pa = getsubsegfarorg(&checkseg);
      pb = getsubsegfardest(&checkseg);
      //printf("  1st: (%d, %d), 2nd: (%d, %d).\n", pointmark(startpt), 
        //pointmark(endpt), pointmark(pa), pointmark(pb));
      //terminatetetgen(3);
	  return;
    }
    across_edge_count++;
  }
  
  pc = apex(*searchtet);
  ang = interiorangle(pc, startpt, endpt, NULL);
  if (ang > angmax) {
    angmax = ang;
    *refpt = pc;
  }
  reftet = *searchtet; // Save the tet containing the refpt.

  // Search intersecting faces along the segment.
  while (1) {

    pd = oppo(*searchtet);

    if (b->verbose > 2) {
      //printf("      Passing face (%d, %d, %d, %d), dir(%d).\n", pointmark(pa),
        //pointmark(pb), pointmark(pc), pointmark(pd), (int) dir);
    }
    across_face_count++;

    // Stop if we meet 'endpt'.
    if (pd == endpt) break;

    ang = interiorangle(pd, startpt, endpt, NULL);
    if (ang > angmax) {
      angmax = ang;
      *refpt = pd;
      reftet = *searchtet;
    }

    // Find a face intersecting the segment.
    if (dir == INTERFACE) {
      // One of the three oppo faces in 'searchtet' intersects the segment.
      neightet.tet = searchtet->tet;
      neightet.ver = 0;
      for (i = 0; i < 3; i++) {
        neightet.loc = locpivot[searchtet->loc][i];
        pa = org(neightet);
        pb = dest(neightet);
        pc = apex(neightet);
        pd = oppo(neightet); // The above point.
        if (tri_edge_test(pa, pb, pc, startpt, endpt, pd, 1, types, poss)) {
          dir = (enum interresult) types[0];
          pos = poss[0];
          break;
        } else {
          dir = DISJOINT;
          pos = 0;
        }
      }
      //assert(dir != DISJOINT);  // SELF_CHECK
    } else { // dir == ACROSSEDGE
      // Check the two opposite faces (of the edge) in 'searchtet'.
      neightet = *searchtet;
      neightet.ver = 0;
      for (i = 0; i < 2; i++) {
        neightet.loc = locverpivot[searchtet->loc][searchtet->ver][i];
        pa = org(neightet);
        pb = dest(neightet);
        pc = apex(neightet);
        pd = oppo(neightet); // The above point.
        if (tri_edge_test(pa, pb, pc, startpt, endpt, pd, 1, types, poss)) {
          dir = (enum interresult) types[0];
          pos = poss[0];
          break;
        } else {
          dir = DISJOINT;
          pos = 0;
        }
      }
      if (dir == DISJOINT) {
        // No intersection. Go to the next tet.
        dir = INTEREDGE;
        tfnextself(*searchtet);
        continue;
      }
    }

    if (dir == INTERVERT) {
      // This segment passing a vertex. Choose it and return.
      for (i = 0; i < pos; i++) {
        enextself(neightet);
      }
      pd = org(neightet);
      if (b->verbose > 2) {
        angmax = interiorangle(pd, startpt, endpt, NULL);
      }
      *refpt = pd;
      break;
    }
    if (dir == INTEREDGE) {
      // Get the edge intersects with the segment.
      for (i = 0; i < pos; i++) {
        enextself(neightet);
      }
    }
    // Go to the next tet.
    symedge(neightet, *searchtet);

    if (dir == INTEREDGE) {
      // Check whether two segments are intersecting.
      tsspivot1(*searchtet, checkseg);
      if (checkseg.sh != dummysh) {
        //printf("Error:  Invalid PLC! Two segments intersect.\n");
        startpt = getsubsegfarorg(sseg);
        endpt = getsubsegfardest(sseg);
        pa = getsubsegfarorg(&checkseg);
        pb = getsubsegfardest(&checkseg);
        //printf("    1st: (%d, %d), 2nd: (%d, %d).\n", pointmark(startpt), 
          //pointmark(endpt), pointmark(pa), pointmark(pb));
        //terminatetetgen(3);
		return;
      }
      across_edge_count++;
    }

  } // while (1)

  // dir is either ACROSSVERT, or ACROSSEDGE, or ACROSSFACE.
  if (b->verbose > 2) {
    //printf("      Refpt %d (%g), visited %ld faces.\n", pointmark(*refpt),
//      angmax / PI * 180.0, across_face_count - facecount);
  }
  if (across_face_count - facecount > across_max_count) {
    across_max_count = across_face_count - facecount;
  }

  *searchtet = reftet;
  return dir;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// getsegmentsplitpoint()    Calculate a split point in the given segment.   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::getsegmentsplitpoint2(face* sseg, point refpt, REAL* vt)
{
  point ei, ej, ek;
  REAL split, L, d, d1, d2, d3;
  int stype, sign;
  int i;

  // Decide the type of this segment.
  sign = 1;
  ei = sorg(*sseg);
  ej = sdest(*sseg);

  if (pointtype(ei) == ACUTEVERTEX) {
    if (pointtype(ej) == ACUTEVERTEX) {
      // Both ei and ej are ACUTEVERTEX.
      stype = 0;
    } else {
      // ej is either a NACUTEVERTEX or a STEINERVERTEX.
      stype = 1;
    }
  } else {
    if (pointtype(ei) == NACUTEVERTEX) {
      if (pointtype(ej) == ACUTEVERTEX) {
        stype = 1; sign = -1;
      } else {
        if (pointtype(ej) == NACUTEVERTEX) {
          // Both ei and ej are non-acute.
          stype = 0;
        } else {
          // ej is a STEINERVETEX.
          ek = getsubsegfardest(sseg);
          if (pointtype(ek) == ACUTEVERTEX) {
            stype = 1; sign = -1;
          } else {
            stype = 0;
          }
        }
      }
    } else {
      // ei is a STEINERVERTEX.
      if (pointtype(ej) == ACUTEVERTEX) {
        stype = 1; sign = -1;
      } else {
        ek = getsubsegfarorg(sseg);
        if (pointtype(ej) == NACUTEVERTEX) {
          if (pointtype(ek) == ACUTEVERTEX) {
            stype = 1;
          } else {
            stype = 0;
          }
        } else {
          // Both ei and ej are STEINERVETEXs. ei has priority.
          if (pointtype(ek) == ACUTEVERTEX) {
            stype = 1;
          } else {
            ek = getsubsegfardest(sseg);
            if (pointtype(ek) == ACUTEVERTEX) {
              stype = 1; sign = -1;
            } else {
              stype = 0;
            }
          }
        }
      }
    }
  }

  // Adjust the endpoints: ei, ej.
  if (sign == -1) {
    sesymself(*sseg);
    ei = sorg(*sseg);
    ej = sdest(*sseg);
  }

  if (b->verbose > 1) {
    //printf("    Split a type-%d seg(%d, %d) ref(%d)", stype,
      //pointmark(ei), pointmark(ej), pointmark(refpt));
    if (stype) {
      ek = getsubsegfarorg(sseg);
      //printf(" ek(%d)", pointmark(ek));
    }
    //printf(".\n");
  }

  // Calculate the split point.
  if (stype == 0) {
    // Use rule-1.
    L = DIST(ei, ej);
    d1 = DIST(ei, refpt);
    d2 = DIST(ej, refpt);
    if (d1 < d2) {
      // Choose ei as center.
      if (d1 < 0.5 * L) {
        split = d1 / L;
        // Adjust split if it is close to middle. (2009-02-01)
        if ((split > 0.4) || (split < 0.6)) split = 0.5;
      } else {
        split = 0.5;
      }
      for (i = 0; i < 3; i++) {
        vt[i] = ei[i] + split * (ej[i] - ei[i]);
      }
    } else {
      // Choose ej as center.
      if (d2 < 0.5 * L) {
        split = d2 / L;
        // Adjust split if it is close to middle. (2009-02-01)
        if ((split > 0.4) || (split < 0.6)) split = 0.5;
      } else {
        split = 0.5;
      }
      for (i = 0; i < 3; i++) {
        vt[i] = ej[i] + split * (ei[i] - ej[i]);
      }
    }
    r1count++;
  } else {
    // Use rule-2.
    ek = getsubsegfarorg(sseg);
    L = DIST(ek, ej);
    d = DIST(ek, refpt);
    split = d / L;
    for (i = 0; i < 3; i++) {
      vt[i] = ek[i] + split * (ej[i] - ek[i]);
    }
    d1 = DIST(vt, refpt);
    d2 = DIST(vt, ej);
    if (d1 > d2) {
      // Use rule-3.
      d3 = DIST(ei, refpt);
      if (d1 < 0.5 * d3) {
        split = (d - d1) / L;
      } else {
        split = (d - 0.5 * d3) / L;
      }
      for (i = 0; i < 3; i++) {
        vt[i] = ek[i] + split * (ej[i] - ek[i]);
      }
    }
    d1 > d2 ? r3count++ : r2count++;
  }

  if (b->verbose > 1) {
    //printf("    split (%g), vt (%g, %g, %g).\n", split, vt[0], vt[1], vt[2]);
  }
}

void tetgenmesh::getsegmentsplitpoint3(face* seg, point refpt, REAL* steinpt)
{
  point ei, ej;
  REAL Li, Lj, L;
  REAL t;
  int i;

  ei = sorg(*seg);
  ej = sdest(*seg);

  if (b->verbose > 1) {
    //printf("      Get Steiner point on seg (%d, %d).\n", pointmark(ei),
           //pointmark(ej));
  }

  if (refpt != NULL) {
    // Let ei be the closer one to refpt.
    Li = distance(ei, refpt);
    Lj = distance(ej, refpt);
    if (Li > Lj) {
      // Swap ei and ej;
      sesymself(*seg);
      ei = sorg(*seg);
      ej = sdest(*seg);
      L = Li;
      Li = Lj;
      Lj = L;
    }
    if (pointtype(ei) == ACUTEVERTEX) {
      // Cut the segment by a sphere centered at ei with radius Li. 
      L = distance(ei, ej);
      t = Li / L; // t \in (0, 1).
      for (i = 0; i < 3; i++) {
        steinpt[i] = ei[i] + t * (ej[i] - ei[i]);
      }
      // Re-use Li and Lj;
      Li = distance(steinpt, refpt);
      Lj = distance(steinpt, ej);
      if (Li > Lj) {
        // Avoid to create a very short edge at ej.
        t = 0.5;
        for (i = 0; i < 3; i++) {
          steinpt[i] = ei[i] + t * (ej[i] - ei[i]);
        }
        r3count++;
      } else {
        r2count++;
      }
    } else {
      // Cut the segment by the projection point of refpt.
      projpt2edge(refpt, ei, ej, steinpt);
      // Only for report.
      L = distance(ei, ej);
      Li = distance(steinpt, ei);
      t = Li / L;
      r1count++;
    }
  } else {
    // Split the point at the middle.
    t = 0.5;
    for (i = 0; i < 3; i++) {
      steinpt[i] = ei[i] + t * (ej[i] - ei[i]);
    }
    r1count++;
  } // if (refpt == NULL)

  if (pointtype(steinpt) == UNUSEDVERTEX) {
    setpointtype(steinpt, FREESEGVERTEX);
  }

  if (b->verbose > 2) {
    //printf("      Split at t(%g).\n", t);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// delaunizesegments()    Recover segments in a Delaunay tetrahedralization. //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::delaunizesegments2()
{
  triface searchtet;
  face splitsh;
  face *psseg, sseg; // *parysh;
  point refpt, newpt;
  enum interresult dir;
  bool visflag;

  if (b->verbose) {
    //printf("  Delaunizing segments.\n");
  }

  // Loop until 'subsegstack' is empty.
  while (subsegstack->objects > 0l) {
    // seglist is used as a stack.
    subsegstack->objects--;
    psseg = (face *) fastlookup(subsegstack, subsegstack->objects);
    sseg = *psseg;

    if (!sinfected(sseg)) continue; // Not a missing segment.
    suninfect(sseg);

    // Insert the segment.
    searchtet.tet = NULL;
    dir = scoutsegment2(&sseg, &searchtet, &refpt);

    if (dir != SHAREEDGE) {
      // The segment is missing, split it.
      spivot(sseg, splitsh);
      if (dir != INTERVERT) {
        // Create the new point.
        makepoint(&newpt);
        getsegmentsplitpoint3(&sseg, refpt, newpt);
        setpointtype(newpt, FREESEGVERTEX);
        setpoint2sh(newpt, sencode(sseg));
        // Split the segment by newpt.
        sinsertvertex(newpt, &splitsh, &sseg, true, false);
        // Insert newpt into the DT. If 'checksubfaces == 1' the current
        //   mesh is constrained Delaunay (but may not Delaunay).
        visflag = (checksubfaces == 1);
        insertvertexbw(newpt, &searchtet, true, visflag, false, false);
      } else {
        /*if (getpointtype(refpt) != ACUTEVERTEX) {
          setpointtype(refpt, RIDGEVERTEX);
        }
        // Split the segment by refpt.
        sinsertvertex(refpt, &splitsh, &sseg, true, false);*/
        //printf("Error:  Invalid PLC! A point and a segment intersect.\n");
        point pa, pb;
        pa = getsubsegfarorg(&sseg);
        pb = getsubsegfardest(&sseg);
        //printf("  Point: %d. Segment: (%d, %d).\n", pointmark(refpt),
         // pointmark(pa), pointmark(pb));
        //terminatetetgen(3);
		return;
      }
    }
  }

  if (b->verbose) {
    //printf("  %ld protecting points.\n", r1count + r2count + r3count);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// scoutsubface()    Look for a given subface in the tetrahedralization T.   //
//                                                                           //
// 'ssub' is the subface, denoted as abc. If abc exists in T, it is 'locked' //
// at the place where the two tets sharing at it.                            //
//                                                                           //
// 'convexflag' indicates the current mesh is convex (1) or non-convex (0).  //
//                                                                           //
// The returned value indicates one of the following cases:                  //
//   - SHAREFACE, abc exists and is inserted;                                //
//   - TOUCHEDGE, a vertex (the origin of 'searchtet') lies on ab.           //
//   - EDGETRIINT, all three edges of abc are missing.                       //
//   - ACROSSTET, a tet (in 'searchtet') crosses the facet containg abc.     //
//                                                                           //
// If the retunred value is ACROSSTET, the subface is missing.  'searchtet'  //
// returns a tet which shares the same edge as 'pssub'.                      //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

enum tetgenmesh::interresult tetgenmesh::scoutsubface(face* pssub,
  triface* searchtet, int convexflag)
{
  triface spintet;
  face checksh;
  point pa, pb, pc, pd;
  enum interresult dir;
  int hitbdry;
  int i;

  if ((searchtet->tet == NULL) || (searchtet->tet == dummytet)) {
    // Search an edge of 'ssub' in tetrahedralization.
    pssub->shver = 0;
    for (i = 0; i < 3; i++) {
      pa = sorg(*pssub);
      pb = sdest(*pssub);
      // Get a tet whose origin is pa.
      point2tetorg(pa, *searchtet);
      // Search the edge from pa->pb.
      dir = finddirection2(searchtet, pb);
      if (dir == INTERVERT) {
        if (dest(*searchtet) == pb) {
          // Found the edge. Break the loop.
          break;
        } else {
          // A vertex lies on the search edge. Return it.
          enextself(*searchtet);
          return TOUCHEDGE;
        }
      } else if (dir == BELOWHULL2) {
        if (convexflag > 0) {
          //assert(0);
        }
        // The domain is non-convex, and we got stucked at a boundary face.
        point2tetorg(pa, *searchtet);
        dir = finddirection3(searchtet, pb);
        if (dir == INTERVERT) {
          if (dest(*searchtet) == pb) {
            // Found the edge. Break the loop.
            break;
          } else {
            // A vertex lies on the search edge. Return it.
            enextself(*searchtet);
            return TOUCHEDGE;
          }
        }
      }
      senextself(*pssub);
    }
    if (i == 3) {
      // None of the three edges exists.
      return EDGETRIINT; // ab intersects the face in 'searchtet'.
    }
  } else {
    // 'searchtet' holds the current edge of 'pssub'.
    pa = org(*searchtet);
    pb = dest(*searchtet);
  }

  pc = sapex(*pssub);

  if (b->verbose > 1) {
    //printf("    Scout subface (%d, %d, %d) (%ld).\n", pointmark(pa),
      //pointmark(pb), pointmark(pc), subfacstack->objects);
  }

  // Searchtet holds edge pa->pb. Search a face with apex pc.
  spintet = *searchtet;
  pd = apex(spintet);
  hitbdry = 0;
  while (1) {
    if (pd == pc) {
      // Found! Insert the subface.
      tspivot(spintet, checksh); // SELF_CHECK
      if (checksh.sh == dummysh) {
        // Comment: here we know that spintet and pssub refer to the same
        //   edge and the same DIRECTION: pa->pb.
        if ((spintet.ver & 1) == 1) {
          // Stay in CCW edge ring.
          esymself(spintet);
        }
        if (sorg(*pssub) != org(spintet)) {
          sesymself(*pssub);
        }
        tsbond(spintet, *pssub);
        symself(spintet);
        if (spintet.tet != dummytet) {
          tspivot(spintet, checksh); // SELF_CHECK
          //assert(checksh.sh == dummysh); // SELF_CHECK
          sesymself(*pssub);
          tsbond(spintet, *pssub);
        }
        return SHAREFACE;
      } else {
        *searchtet = spintet;
        if (checksh.sh != pssub->sh) {
          // Another subface is laready inserted.
          // Comment: This is possible when there are faked tets.
          return COLLISIONFACE;
        } else {
          // The subface has already been inserted (when you do check). 
          return SHAREFACE;
        }
      }
    }
    if (!fnextself(spintet)) {
      hitbdry++;
      if (hitbdry == 2) break;
      esym(*searchtet, spintet);
      if (!fnextself(spintet)) break;
    }
    pd = apex(spintet);
    if (pd == apex(*searchtet)) break;
  }

  return INTERTET;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// scoutcrosstet()    Scout a tetrahedron across a facet.                    //
//                                                                           //
// A subface (abc) of the facet (F) is given in 'pssub', 'searchtet' holds   //
// the edge ab, it is the tet starting the search.  'facpoints' contains all //
// points which are co-facet with a, b, and c.                               //
//                                                                           //
// The subface (abc) was produced by a 2D CDT algorithm under the Assumption //
// that F is flat. In real data, however, F may not be strictly flat.  Hence //
// a tet (abde) that crosses abc may be in one of the two cases: (i) abde    //
// intersects F in its interior, or (ii) abde intersects F on its boundary.  //
// In case (i) F (or part of it) is missing in DT and needs to be recovered. //
// In (ii) F is not missing, the surface mesh of F needs to be adjusted.     //
//                                                                           //
// This routine distinguishes the two cases by the returned value, which is  //
//   - INTERTET, if it is case (i), 'searchtet' is abde, d and e lies below  //
//     and above abc, respectively, neither d nor e is dummypoint; or        //
//   - INTERFACE, if it is case (ii), 'searchtet' is abde, where the face    //
//     abd intersects abc, i.e., d is co-facet with abc, e may be co-facet   //
//     with abc or dummypoint.                                               //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

enum tetgenmesh::interresult tetgenmesh::scoutcrosstet(face *pssub, 
  triface* searchtet, arraypool* facpoints)
{
  triface spintet, crossface;
  point pa, pb, pc, pd, pe;
  REAL ori, ori1, len, n[3];
  REAL r, dr, drmin;
  bool cofacetflag;
  int hitbdry;
  int i;

  if (facpoints != NULL) {
    // Infect all vertices of the facet.
    for (i = 0; i < (int) facpoints->objects; i++) {
      pd = * (point *) fastlookup(facpoints, i);
      pinfect(pd);
    }
  }

  // Search an edge crossing the facet containing abc.
  if (searchtet->ver & 01) {
    esymself(*searchtet); // Adjust to 0th edge ring.
    sesymself(*pssub);
  }

  pa = sorg(*pssub);
  pb = sdest(*pssub);
  pc = sapex(*pssub);

  // 'searchtet' refers to edge pa->pb. 
  //assert(org(*searchtet) == pa);
  //assert(dest(*searchtet) == pb);

  // Search an apex lies below the subface. Note that such apex may not
  //   exist which indicates there is a co-facet apex.
  cofacetflag = false;
  pd = apex(*searchtet);
  spintet = *searchtet;
  hitbdry = 0;
  while (1) {
    ori = orient3d(pa, pb, pc, pd);
    if ((ori != 0) && pinfected(pd)) {
      ori = 0; // Force d be co-facet with abc.
    }
    if (ori > 0) {
      break; // Found a lower point (the apex of spintet).
    }
    // Go to the next face.
    if (!fnextself(spintet)) {
      hitbdry++;
      if (hitbdry == 2) {
        cofacetflag = true; break; // Not found.
      }
      esym(*searchtet, spintet);
      if (!fnextself(spintet)) {
        cofacetflag = true; break; // Not found.
      }
    }
    pd = apex(spintet);
    if (pd == apex(*searchtet)) {
      cofacetflag = true; break; // Not found.
    }
  }

  if (!cofacetflag) {
    if (hitbdry > 0) {
      // The edge direction is reversed, which means we have to reverse
      //   the face rotation direction to find the crossing edge d->e.
      esymself(spintet);
    }
    // Keep the edge a->b be in the CCW edge ring of spintet.
    if (spintet.ver & 1) {
      symedgeself(spintet);
      //assert(spintet.tet != dummytet);
    }
    // Search a tet whose apex->oppo crosses the face [a, b, c].
    //   -- spintet is a face [a, b, d]. 
    //   -- the apex (d) of spintet is below [a, b, c].
    while (1) {
      pe = oppo(spintet);
      ori = orient3d(pa, pb, pc, pe);
      if ((ori != 0) && pinfected(pe)) {
        ori = 0; // Force it to be a coplanar point.
      }
      if (ori == 0) {
        cofacetflag = true; 
        break; // Found a co-facet point.
      }
      if (ori < 0) {
        *searchtet = spintet;
        break;  // Found. edge [d, e].
      }
      // Go to the next tet.
      tfnextself(spintet);
      if (spintet.tet == dummytet) {
        cofacetflag = true; 
        break; // There is a co-facet point.
      }
    }
    // Now if "cofacetflag != true", searchtet contains a cross tet (abde), 
    //   where d and e lie below and above abc, respectively, and 
    //   orient3d(a, b, d, e) < 0.
  }

  if (cofacetflag) {
    // There are co-facet points. Calculate a point above the subface.
    facenormal2(pa, pb, pc, n, 1);
    len = sqrt(DOT(n, n));
    n[0] /= len;
    n[1] /= len;
    n[2] /= len;
    len = DIST(pa, pb);
    len += DIST(pb, pc);
    len += DIST(pc, pa);
    len /= 3.0;
    dummypoint[0] = pa[0] + len * n[0];
    dummypoint[1] = pa[1] + len * n[1];
    dummypoint[2] = pa[2] + len * n[2];
    // Search a co-facet point d, s.t. (i) [a, b, d] intersects [a, b, c],
    //   AND (ii) a, b, c, d has the closet circumradius of [a, b, c].
    // NOTE: (ii) is needed since there may be several points satisfy (i).
    //   For an example, see file2.poly.
    circumsphere(pa, pb, pc, NULL, n, &r);
    crossface.tet = NULL;
    pe = apex(*searchtet);
    spintet = *searchtet;
    hitbdry = 0;
    while (1) {
      pd = apex(spintet);
      ori = orient3d(pa, pb, pc, pd);
      if ((ori == 0) || pinfected(pd)) {
        ori1 = orient3d(pa, pb, dummypoint, pd);
        if (ori1 > 0) {
          // [a, b, d] intersects with [a, b, c].
          if (pinfected(pd)) {
            len = DIST(n, pd);
            dr = fabs(len - r);
            if (crossface.tet == NULL) {
              // This is the first cross face.
              crossface = spintet;
              drmin = dr;
            } else {
              if (dr < drmin) {
                crossface = spintet;
                drmin = dr;
              }
            }
          } else {
            //assert(ori == 0); // SELF_CHECK
            // Found a coplanar but not co-facet point (pd).
            //printf("Error:  Invalid PLC! A point and a subface intersect\n");
            // get_origin_facet_corners(pssub, &pa, &pb, &pc);
            //printf("  Point %d. Subface (#%d) (%d, %d, %d)\n", 
             // pointmark(pd), shellmark(*pssub), pointmark(pa), pointmark(pb),
              //pointmark(pc));
            terminatetetgen(3);
          }
        }
      }
      // Go to the next face.
      if (!fnextself(spintet)) {
        hitbdry++;
        if (hitbdry == 2) break;
        esym(*searchtet, spintet);
        if (!fnextself(spintet)) break;
      }
      if (apex(spintet) == pe) {
        break;
      }
    }
    if(crossface.tet == NULL) {
      //assert(crossface.tet != NULL); // Not handled yet.
    }
    *searchtet = crossface;
    dummypoint[0] = dummypoint[1] = dummypoint[2] = 0;
  }

  if (cofacetflag) {
    if (b->verbose > 1) {
      //printf("    Found a co-facet face (%d, %d, %d) op (%d).\n", 
        //pointmark(pa), pointmark(pb), pointmark(apex(*searchtet)), 
        //pointmark(oppo(*searchtet)));
    }
    if (facpoints != NULL) {
      // Unmark all facet vertices.
      for (i = 0; i < (int) facpoints->objects; i++) {
        pd = * (point *) fastlookup(facpoints, i);
        puninfect(pd);
      }
    }
    // Comment: Now no vertex is infected.
    /*if (getpointtype(apex(*searchtet)) == VOLVERTEX) {
      // A vertex lies on the facet.
      enext2self(*searchtet); // org(*searchtet) == pd
      return TOUCHFACE;
    }*/
    return INTERFACE;
  } else {
    // Return a crossing tet.
    if (b->verbose > 1) {
      //printf("    Found a crossing tet (%d, %d, %d, %d).\n", pointmark(pa),
        //pointmark(pb), pointmark(apex(*searchtet)), pointmark(pe));
    }
    // Comment: if facpoints != NULL, co-facet vertices are stll infected.
    //   They will be uninfected in formcavity();
    return INTERTET; // abc intersects the volume of 'searchtet'.
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// recoversubfacebyflips()   Recover a subface by flips in the surface mesh. //
//                                                                           //
// A subface [a, b, c] ('pssub') intersects with a face [a, b, d] ('cross-   //
// face'), where a, b, c, and d belong to the same facet.  It indicates that //
// the face [a, b, d] should appear in the surface mesh.                     //
//                                                                           //
// This routine recovers [a, b, d] in the surface mesh through a sequence of //
// 2-to-2 flips. No Steiner points is needed. 'pssub' returns [a, b, d].     //
//                                                                           //
// If 'facfaces' is not NULL, all flipped subfaces are queued for recovery.  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::recoversubfacebyflips(face* pssub, triface* crossface, 
  arraypool *facfaces)
{
  triface neightet;
  face flipfaces[2], *parysh;
  face checkseg;
  point pa, pb, pc, pd, pe;
  REAL ori, len, n[3];

  // Get the missing subface is [a, b, c].
  pa = sorg(*pssub);
  pb = sdest(*pssub);
  pc = sapex(*pssub);

  // The crossface is [a, b, d, e].
  // //assert(org(*crossface) == pa);
  // //assert(dest(*crossface) == pb);
  pd = apex(*crossface);
  pe = dummypoint; // oppo(*crossface);

  if (pe == dummypoint) {
    // Calculate a point above the faces.
    facenormal2(pa, pb, pd, n, 1);
    len = sqrt(DOT(n, n));
    n[0] /= len;
    n[1] /= len;
    n[2] /= len;
    len = DIST(pa, pb);
    len += DIST(pb, pd);
    len += DIST(pd, pa);
    len /= 3.0;
    pe[0] = pa[0] + len * n[0];
    pe[1] = pa[1] + len * n[1];
    pe[2] = pa[2] + len * n[2];
  }

  // Adjust face [a, b, c], so that edge [b, c] crosses edge [a, d].
  ori = orient3d(pb, pc, pe, pd);
  //assert(ori != 0); // SELF_CHECK

  if (ori > 0) {
    // Swap a and b.
    sesymself(*pssub);
    esymself(*crossface); // symedgeself(*crossface);
    pa = sorg(*pssub);
    pb = sdest(*pssub);
    if (pe == dummypoint) {
      pe[0] = pe[1] = pe[2] = 0;
    }
    pe = dummypoint; // oppo(*crossface);
  }

  while (1) {

    // Flip edge [b, c] to edge [a, d].
    senext(*pssub, flipfaces[0]);
    sspivot(flipfaces[0], checkseg); // SELF_CHECK
    //assert(checkseg.sh == dummysh); // SELF_CHECK
    spivot(flipfaces[0], flipfaces[1]);

    stpivot(flipfaces[1], neightet);
    if (neightet.tet != dummytet) {
      // A recovered subface, clean sub<==>tet connections.
      tsdissolve(neightet);
      symself(neightet);
      tsdissolve(neightet);
      stdissolve(flipfaces[1]);
      sesymself(flipfaces[1]);
      stdissolve(flipfaces[1]);
      sesymself(flipfaces[1]);
      // flipfaces[1] refers to edge [b, c] (either b->c or c->b).
    }

    flip22sub(&(flipfaces[0]), NULL);
    flip22count++;

    // Comment: now flipfaces[0] is [d, a, b], flipfaces[1] is [a, d, c].

    // Add them into list (make ensure that they must be recovered).
    facfaces->newindex((void **) &parysh);
    *parysh = flipfaces[0];
    facfaces->newindex((void **) &parysh);
    *parysh = flipfaces[1];

    // Find the edge [a, b].
    senext(flipfaces[0], *pssub);
    //assert(sorg(*pssub) == pa); // SELF_CHECK
    //assert(sdest(*pssub) == pb); // SELF_CHECK

    pc = sapex(*pssub);
    if (pc == pd) break;

    if (pe == dummypoint) {
      // Calculate a point above the faces.
      facenormal2(pa, pb, pd, n, 1);
      len = sqrt(DOT(n, n));
      n[0] /= len;
      n[1] /= len;
      n[2] /= len;
      len = DIST(pa, pb);
      len += DIST(pb, pd);
      len += DIST(pd, pa);
      len /= 3.0;
      pe[0] = pa[0] + len * n[0];
      pe[1] = pa[1] + len * n[1];
      pe[2] = pa[2] + len * n[2];
    }

    while (1) {
      ori = orient3d(pb, pc, pe, pd);
      //assert(ori != 0); // SELF_CHECK
      if (ori > 0) {
        senext2self(*pssub);
        spivotself(*pssub);
        if (sorg(*pssub) != pa) sesymself(*pssub);
        pb = sdest(*pssub);
        pc = sapex(*pssub);
        continue;
      }
      break;
    }
  }

  if (pe == dummypoint) {
    pe[0] = pe[1] = pe[2] = 0;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// formcavity()    Form the cavity of a missing region.                      //
//                                                                           //
// A missing region R is a set of co-facet (co-palanr) subfaces. 'pssub' is  //
// a missing subface [a, b, c]. 'crosstets' contains only one tet, [a, b, d, //
// e], where d and e lie below and above [a, b, c], respectively.  Other     //
// crossing tets are sought from this tet and saved in 'crosstets'.          //
//                                                                           //
// The cavity C is divided into two parts by R,one at top and one at bottom. //
// 'topfaces' and 'botfaces' return the upper and lower boundary faces of C. //
// 'toppoints' contains vertices of 'crosstets' in the top part of C, and so //
// does 'botpoints'. Both 'toppoints' and 'botpoints' contain vertices of R. //
//                                                                           //
// NOTE: 'toppoints' may contain points which are not vertices of any top    //
// faces, and so may 'botpoints'. Such points may belong to other facets and //
// need to be present after the recovery of this cavity (P1029.poly).        //
//                                                                           //
// A pair of boundary faces: 'firsttopface' and 'firstbotface', are saved.   //
// They share the same edge in the boundary of the missing region.           //
//                                                                           //
// 'facpoints' contains all vertices of the facet containing R.  They are    //
// used for searching the crossing tets. On input all vertices are infected. //
// They are uninfected after the cavity is formed.                           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::formcavity(face *pssub, arraypool* crosstets, 
  arraypool* topfaces, arraypool* botfaces, arraypool* toppoints,
  arraypool* botpoints, arraypool* facpoints, arraypool* facfaces)
{
  arraypool *crossedges;
  triface *parytet, crosstet, spintet, neightet, faketet;
  face neighsh, checksh, *parysh;
  face checkseg;
  point pa, pb, pc, pf, pg;
  point pd, pe;
  point *ppt;
  // REAL ori;
  int i, j;

  // For triangle-edge test.
  enum interresult dir;
  int types[2], poss[4];

  // Get the missing subface abc.
  pa = sorg(*pssub);
  pb = sdest(*pssub);
  pc = sapex(*pssub);

  // Comment: Now all facet vertices are infected.

  // Get a crossing tet abde.
  parytet = (triface *) fastlookup(crosstets, 0); // face abd.
  // The edge de crosses the facet. d lies below abc.
  enext2fnext(*parytet, crosstet);
  enext2self(crosstet); 
  esymself(crosstet); // the edge d->e at face [d,e,a]
  infect(crosstet);
  *parytet = crosstet; // Save it in list.

  // Temporarily re-use 'topfaces' for storing crossing edges.
  crossedges = topfaces;
  crossedges->newindex((void **) &parytet);
  *parytet = crosstet;

  // Collect all crossing tets.  Each cross tet is saved in the standard
  //   form deab, where de is a corrsing edge, orient3d(d,e,a,b) < 0. 
  // NOTE: hull tets may be collected. See fig/dump-cavity-case2a(b).lua.
  //   Make sure that neither d nor e is dummypoint.
  for (i = 0; i < (int) crossedges->objects; i++) {
    crosstet = * (triface *) fastlookup(crossedges, i);
    // It may already be tested.
    if (!edgemarked(crosstet)) {
      // Collect all tets sharing at the edge.
      pg = apex(crosstet);
      spintet = crosstet;
      while (1) {
        // Mark this edge as tested.
        markedge(spintet);
        if (!infected(spintet)) {
          infect(spintet);
          crosstets->newindex((void **) &parytet);
          *parytet = spintet;
        }
        // Go to the neighbor tet.
        tfnextself(spintet);
        if (spintet.tet != dummytet) {
          // Check the validity of the PLC.
          tspivot(spintet, checksh);
          if (checksh.sh != dummysh) {
            //printf("Error:  Invalid PLC! Two subfaces intersect.\n");
            //printf("  1st (#%4d): (%d, %d, %d)\n", shellmark(*pssub),
             // pointmark(pa), pointmark(pb), pointmark(pc));
            //printf("  2nd (#%4d): (%d, %d, %d)\n", shellmark(checksh),
             // pointmark(sorg(checksh)), pointmark(sdest(checksh)),
             // pointmark(sapex(checksh)));
            //terminatetetgen(3);
			  return;
          }
        } else {
          // Encounter a boundary face.
          //assert(0); // Not handled yet.
        }
        if (apex(spintet) == pg) break;
      }
      // Detect new cross edges. 
      // Comment: A crossing edge must intersect one missing subface of
      //   this facet. We do edge-face tests. 
      pd = org(spintet);
      pe = dest(spintet);
      while (1) {
        // Remember: spintet is edge d->e, d lies below [a, b, c].
        pf = apex(spintet);
        // if (pf != dummypoint) { // Do not grab a hull edge.
          if (!pinfected(pf)) {
            for (j = 0; j < (int) facfaces->objects; j++) {
              parysh = (face *) fastlookup(facfaces, j);
              pa = sorg(*parysh);
              pb = sdest(*parysh);
              pc = sapex(*parysh);
              // Check if pd->pf crosses the facet.
              if (tri_edge_test(pa, pb, pc, pd, pf, NULL, 1, types, poss)) {
                dir = (enum interresult) types[0];
                if ((dir == INTEREDGE) || (dir == INTERFACE)) {
                  // The edge d->f corsses the facet.
                  enext2fnext(spintet, neightet);
                  esymself(neightet); // d->f.
                  // pd must lie below the subface.
                  break;
                }
              }
              // Check if pe->pf crosses the facet.
              if (tri_edge_test(pa, pb, pc, pe, pf, NULL, 1, types, poss)) {
                dir = (enum interresult) types[0];
                if ((dir == INTEREDGE) || (dir == INTERFACE)) {
                  // The edge f->e crosses the face.
                  enextfnext(spintet, neightet);
                  esymself(neightet); // f->e.
                  // pf must lie below the subface.
                  break;
                }
              }
            }
            // There must exist a crossing edge.
            //assert(j < (int) facfaces->objects);
            /*// There exist a crossing edge, either d->f, or f->e.
            ori = orient3d(pa, pb, pc, pf);
            if (ori == 0) {
              //printf("Error:  Invalid PLC! Point and subface intersect.\n");
              //printf("   Point %d, subface (#%4d): (%d, %d, %d)\n", 
                pointmark(pf), shellmark(*pssub), pointmark(pa), 
                pointmark(pb), pointmark(pc));
              terminatetetgen(3);
            }
            if (ori < 0) {
              // The edge d->f corsses the facet.
              enext2fnext(spintet, neightet);
              esymself(neightet); // d->f.
            } else {
              // The edge f->e crosses the face.
              enextfnext(spintet, neightet);
              esymself(neightet); // f->e.
            }
            */
            if (!edgemarked(neightet)) {
              // Add a new cross edge.
              crossedges->newindex((void **) &parytet);
              *parytet = neightet;
            }
          }
        // }
        tfnextself(spintet);
        if (spintet.tet == dummytet) {
          // Encounter a boundary face.
          //assert(0); // Not handled yet.
        }
        if (apex(spintet) == pg) break;
      }
    }
  }

  // Unmark all facet vertices.
  for (i = 0; i < (int) facpoints->objects; i++) {
    ppt = (point *) fastlookup(facpoints, i);
    puninfect(*ppt);
  }

  // Comments: Now no vertex is marked. Next we will mark vertices which 
  //   belong to the top and bottom boundary faces of the cavity and put
  //   them in 'toppopints' and 'botpoints', respectively.

  // All cross tets are found. Unmark cross edges.
  for (i = 0; i < (int) crossedges->objects; i++) {
    crosstet = * (triface *) fastlookup(crossedges, i);
    if (edgemarked(crosstet)) {
      // Add the vertices of the cross edge [d, e] in lists. It must be
      //   that d lies below the facet (i.e., its a bottom vertex).
      //   Note that a cross edge contains no dummypoint.
      pf = org(crosstet);
      // //assert(pf != dummypoint); // SELF_CHECK
      if (!pinfected(pf)) {
        pinfect(pf);
        botpoints->newindex((void **) &ppt); // Add a bottom vertex.
        *ppt = pf;
      }
      pf = dest(crosstet);
      // //assert(pf != dummypoint); // SELF_CHECK
      if (!pinfected(pf)) {
        pinfect(pf);
        toppoints->newindex((void **) &ppt); // Add a top vertex.
        *ppt = pf;
      }
      // Unmark this edge in all tets containing it.
      pg = apex(crosstet);
      spintet = crosstet;
      while (1) {
        //assert(edgemarked(spintet)); // SELF_CHECK
        unmarkedge(spintet);
        tfnextself(spintet); // Go to the neighbor tet.
        if (spintet.tet == dummytet) {
          //assert(0); // Not handled yet.
        }
        if (apex(spintet) == pg) break;
      }
    }
  }

  if (b->verbose > 1) {
    //printf("    Formed cavity: %ld (%ld) cross tets (edges).\n", 
      //crosstets->objects, crossedges->objects);
  }
  crossedges->restart();

  // Find a pair of cavity boundary faces from the top and bottom sides of
  //   the facet each, and they share the same edge. Save them in the
  //   global variables: firsttopface, firstbotface. They will be used in
  //   fillcavity() for gluing top and bottom new tets.
  for (i = 0; i < (int) crosstets->objects; i++) {
    crosstet = * (triface *) fastlookup(crosstets, i);
    enextfnext(crosstet, spintet);
    enextself(spintet);
    symedge(spintet, neightet);
    // if (!infected(neightet)) {
    if ((neightet.tet == dummytet) || !infected(neightet)) {
      // A top face.
      if (neightet.tet == dummytet) {
        // Create a fake tet to hold the boundary face.
        maketetrahedron(&faketet);  // Create a faked tet.
        setorg(faketet, org(spintet));
        setdest(faketet, dest(spintet));
        setapex(faketet, apex(spintet));
        setoppo(faketet, dummypoint);
        bond(faketet, spintet);
        tspivot(spintet, checksh);
        if (checksh.sh != dummysh) {
          sesymself(checksh);
          tsbond(faketet, checksh);
        }
        for (j = 0; j < 3; j++) { // Bond segments.
          tsspivot1(spintet, checkseg);
          if (checkseg.sh != dummysh) {
            tssbond1(faketet, checkseg);
          }
          enextself(spintet);
          enextself(faketet);
        }
        firsttopface = faketet;
      } else {
        firsttopface = neightet;
      }
    } else {
      continue; // Go to the next cross tet.
    }
    enext2fnext(crosstet, spintet);
    enext2self(spintet);
    symedge(spintet, neightet);
    // if (!infected(neightet)) {
    if ((neightet.tet == dummytet) || !infected(neightet)) {
      // A bottom face.
      if (neightet.tet == dummytet) {
        // Create a fake tet to hold the boundary face.
        maketetrahedron(&faketet);  // Create a faked tet.
        setorg(faketet, org(spintet));
        setdest(faketet, dest(spintet));
        setapex(faketet, apex(spintet));
        setoppo(faketet, dummypoint);
        bond(spintet, faketet);
        tspivot(spintet, checksh);
        if (checksh.sh != dummysh) {
          sesymself(checksh);
          tsbond(faketet, checksh);
        }
        for (j = 0; j < 3; j++) { // Bond segments.
          tsspivot1(spintet, checkseg);
          if (checkseg.sh != dummysh) {
            tssbond1(faketet, checkseg);
          }
          enextself(spintet);
          enextself(faketet);
        }
        firstbotface = faketet;
      } else {
        firstbotface = neightet;
      }
    } else {
      continue;
    }
    break;
  }
  //assert(i < (int) crosstets->objects); // SELF_CHECK
  
  // Collect the top and bottom faces and the middle vertices. Since all top
  //   and bottom vertices have been marked in above. Unmarked vertices are
  //   middle vertices.
  // NOTE 1: Hull tets may be collected. Process them as normal one.
  //   (see fig/dump-cavity-case2.lua.)
  // NOTE 2: Some previously recovered subfaces may be completely
  //   contained in a cavity (see fig/dump-cavity-case6.lua). In such case,
  //   we create two faked tets to hold this subface, one at each side.
  //   The faked tets will be removed in fillcavity().
  for (i = 0; i < (int) crosstets->objects; i++) {
    crosstet = * (triface *) fastlookup(crosstets, i);
    enextfnext(crosstet, spintet);
    enextself(spintet);
    symedge(spintet, neightet);
    // if (!infected(neightet)) {
    if ((neightet.tet == dummytet) || !infected(neightet)) {
      // A top face.
      topfaces->newindex((void **) &parytet);
      if (neightet.tet == dummytet) {
        // Create a fake tet to hold the boundary face.
        maketetrahedron(&faketet);  // Create a faked tet.
        setorg(faketet, org(spintet));
        setdest(faketet, dest(spintet));
        setapex(faketet, apex(spintet));
        setoppo(faketet, dummypoint);
        bond(spintet, faketet);
        tspivot(spintet, checksh);
        if (checksh.sh != dummysh) {
          sesymself(checksh);
          tsbond(faketet, checksh);
        }
        for (j = 0; j < 3; j++) { // Bond segments.
          tsspivot1(spintet, checkseg);
          if (checkseg.sh != dummysh) {
            tssbond1(faketet, checkseg);
          }
          enextself(spintet);
          enextself(faketet);
        }
        *parytet = faketet;
      } else {
        *parytet = neightet;
      }
    } else {
      if ((neightet.tet != dummytet) && infected(neightet)) { 
        // Check if this side is a subface.
        tspivot(spintet, neighsh);
        if (neighsh.sh != dummysh) {
          // Found a subface (inside the cavity)!
          maketetrahedron(&faketet);  // Create a faked tet.
          setorg(faketet, org(spintet));
          setdest(faketet, dest(spintet));
          setapex(faketet, apex(spintet));
          setoppo(faketet, dummypoint);
          marktest(faketet);  // To distinguish it from other faked tets.
          sesymself(neighsh);
          tsbond(faketet, neighsh); // Let it hold the subface.
          for (j = 0; j < 3; j++) { // Bond segments.
            tsspivot1(spintet, checkseg);
            if (checkseg.sh != dummysh) {
              tssbond1(faketet, checkseg);
            }
            enextself(spintet);
            enextself(faketet);
          }
          // Add a top face (at faked tet).
          topfaces->newindex((void **) &parytet);
          *parytet = faketet;
        }
      }
    }
    enext2fnext(crosstet, spintet);
    enext2self(spintet);
    symedge(spintet, neightet);
    // if (!infected(neightet)) {
    if ((neightet.tet == dummytet) || !infected(neightet)) {
      // A bottom face.
      botfaces->newindex((void **) &parytet);
      if (neightet.tet == dummytet) {
        // Create a fake tet to hold the boundary face.
        maketetrahedron(&faketet);  // Create a faked tet.
        setorg(faketet, org(spintet));
        setdest(faketet, dest(spintet));
        setapex(faketet, apex(spintet));
        setoppo(faketet, dummypoint);
        bond(spintet, faketet);
        tspivot(spintet, checksh);
        if (checksh.sh != dummysh) {
          sesymself(checksh);
          tsbond(faketet, checksh);
        }
        for (j = 0; j < 3; j++) { // Bond segments.
          tsspivot1(spintet, checkseg);
          if (checkseg.sh != dummysh) {
            tssbond1(faketet, checkseg);
          }
          enextself(spintet);
          enextself(faketet);
        }
        *parytet = faketet;
      } else {
        *parytet = neightet;
      }
    } else {
      if ((neightet.tet != dummytet) && infected(neightet)) {
        tspivot(spintet, neighsh);
        if (neighsh.sh != dummysh) {
          // Found a subface (inside the cavity)!
          maketetrahedron(&faketet);  // Create a faked tet.
          setorg(faketet, org(spintet));
          setdest(faketet, dest(spintet));
          setapex(faketet, apex(spintet));
          setoppo(faketet, dummypoint);
          marktest(faketet);  // To distinguish it from other faked tets.
          sesymself(neighsh);
          tsbond(faketet, neighsh); // Let it hold the subface.
          for (j = 0; j < 3; j++) { // Bond segments.
            tsspivot1(spintet, checkseg);
            if (checkseg.sh != dummysh) {
              tssbond1(faketet, checkseg);
            }
            enextself(spintet);
            enextself(faketet);
          }
          // Add a bottom face (at faked tet).
          botfaces->newindex((void **) &parytet);
          *parytet = faketet;
        }
      }
    }
    // Add middle vertices if there are (skip dummypoint).
    pf = org(spintet);
    if (!pinfected(pf)) {
      // if (pf != dummypoint) {
        pinfect(pf);
        botpoints->newindex((void **) &ppt); // Add a bottom vertex.
        *ppt = pf;
        toppoints->newindex((void **) &ppt); // Add a top vertex.
        *ppt = pf;
      // }
    }
    pf = dest(spintet);
    if (!pinfected(pf)) {
      // if (pf != dummypoint) {
        pinfect(pf);
        botpoints->newindex((void **) &ppt); // Add a bottom vertex.
        *ppt = pf;
        toppoints->newindex((void **) &ppt); // Add a top vertex.
        *ppt = pf;
      // }
    }
  }

  // Unmark all collected top, bottom, and middle vertices.
  for (i = 0; i < (int) toppoints->objects; i++) {
    ppt = (point *) fastlookup(toppoints, i);
    puninfect(*ppt);
  }
  for (i = 0; i < (int) botpoints->objects; i++) {
    ppt = (point *) fastlookup(botpoints, i);
    puninfect(*ppt);
  }
  // Comments: Now no vertex is marked.
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// delaunizecavity()    Fill a cavity by Delaunay tetrahedra.                //
//                                                                           //
// The tetrahedralizing cavity is the half (top or bottom part) of the whole //
// cavity.  The boundary faces of the half cavity are given in 'cavfaces',   //
// the bounday faces of the internal facet are not given.  These faces will  //
// be recovered later in fillcavity().                                       //
//                                                                           //
// This routine first constructs the DT of the vertices by the Bowyer-Watson //
// algorithm.  Then it identifies the boundary faces of the cavity in DT.    //
// The DT is returned in 'newtets'.                                          //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::delaunizecavity(arraypool *cavpoints, arraypool *cavfaces, 
  arraypool *cavshells, arraypool *newtets, arraypool *crosstets,
  arraypool *misfaces)
{
  triface *parytet, searchtet, neightet, spintet, *parytet1;
  triface newtet, faketet;
  face checksh, tmpsh, *parysh;
  face checkseg;
  point pa, pb, pc, pd, pt[3], *parypt;
  // badface *newflipface;
  enum interresult dir;
  REAL ori;
  // int miscount;
  int i, j, k;

  if (b->verbose > 1) {
    //printf("    Delaunizing cavity: %ld points, %ld faces.\n", 
      //cavpoints->objects, cavfaces->objects);
  }

  // Get four non-coplanar points (no dummypoint).
  parytet = (triface *) fastlookup(cavfaces, 0);
  pa = org(*parytet);
  pb = dest(*parytet);
  pc = apex(*parytet);
  pinfect(pa);
  pinfect(pb);
  pinfect(pc);
  pd = NULL;
  for (i = 1; i < (int) cavfaces->objects; i++) {
    parytet = (triface *) fastlookup(cavfaces, i);
    pt[0] = org(*parytet);
    pt[1] = dest(*parytet);
    pt[2] = apex(*parytet);
    for (j = 0; j < 3; j++) {
      // if (pt[j] != dummypoint) { // Do not include a hull point.
        if (!pinfected(pt[j])) {
          ori = orient3d(pa, pb, pc, pt[j]);
          if (ori != 0) {
            pd = pt[j];
            if (ori > 0) {  // Swap pa and pb.
              pt[j] = pa; pa = pb; pb = pt[j]; 
            }
            break;
          }
        }
      // }
    }
    if (pd != NULL) break;
  }
  //assert(i < (int) cavfaces->objects); // SELF_CHECK
  pinfect(pd);

  // Create an init DT.
  // initialDT(pa, pb, pc, pd);
  // Create the initial tet.
  maketetrahedron(&newtet);
  if (b->verbose > 2) {
    //printf("  Create the first tet (%d, %d, %d, %d).\n",
          // pointmark(pa), pointmark(pb), pointmark(pc), pointmark(pd));
  }
  setorg(newtet, pa);
  setdest(newtet, pb);
  setapex(newtet, pc);
  setoppo(newtet, pd);
  // Update the point-to-tet map.
  setpoint2tet(pa, encode(newtet));
  setpoint2tet(pb, encode(newtet));
  setpoint2tet(pc, encode(newtet));
  setpoint2tet(pd, encode(newtet));
  // Bond to 'dummytet' for point location.
  dummytet[0] = encode(newtet);
  recenttet = newtet;
  // At init, all faces of this tet are hull faces.
  hullsize = 4;

  for (i = 0; i < (int) cavpoints->objects; i++) {
    pt[0] = * (point *) fastlookup(cavpoints, i);
    //assert(pt[0] != dummypoint); // SELF_CHECK
    if (!pinfected(pt[0])) {
      searchtet = recenttet;
      insertvertexbw(pt[0], &searchtet, true, false, false, false);
    } else {
      puninfect(pt[0]); // It is already inserted.
    }
  }
  // Comment: All vertices of the cavity are NOT marked.

  while (1) {

  // Identify boundary faces. Remember interior tets. Save missing faces.
  // For each identified boundary face in the new DT, we insert a subface
  //   temporarily at that place. The subface also contains a pointer to 
  //   the adjacent tet outside of the cavity. We save the temp subface
  //   with its side facing to the interior of the cavity.
  for (i = 0; i < (int) cavfaces->objects; i++) {
    parytet = (triface *) fastlookup(cavfaces, i);
    // Skip an interior face (due to the enlargement of the cavity).
    if (infected(*parytet)) continue;
    // Choose the CCW edge ring.
    parytet->ver = 4;
    pt[0] = org(*parytet);
    pt[1] = dest(*parytet);
    pt[2] = apex(*parytet);
    // Create a temp subface.
    makeshellface(subfaces, &tmpsh);
    // setshvertices(tmpsh, pt[0], pt[1], pt[2]);
    setsorg(tmpsh, pt[0]);
    setsdest(tmpsh, pt[1]);
    setsapex(tmpsh, pt[2]);
    // Comment: This side of tmpsh faces to the outside of the cavity. 
    // Insert tmpsh in DT.
    searchtet.tet = NULL; 
    dir = scoutsubface(&tmpsh, &searchtet, 1);
    if (dir == SHAREFACE) {
      // Let tmpsh face to the interior tet of the cavity.
      if (sorg(tmpsh) == pt[0]) {
        sesymself(tmpsh);
      }
      //assert(sorg(tmpsh) == pt[1]);
      //assert(sdest(tmpsh) == pt[0]);
    } else if (dir == COLLISIONFACE) {
      // A subface is already inserted. This case can only happen when there
      //   exist a subface inside the cavity, and two faked tets were created
      //   for protecting such a subface (see fig/dum-cavity-case6).
      //assert(oppo(*parytet) == dummypoint);
      //assert(marktested(*parytet));
      // This subface is redundant. But it is needed here (to remember the
      //   faked tet and the real subface which is inside the cavity).
      if ((searchtet.ver & 01) != 0) esymself(searchtet);
      // Adjust the searchtet to edge pt[1]->pt[0].
      if (org(searchtet) != pt[1]) {
        symedgeself(searchtet);
        //assert(org(searchtet) == pt[1]); // SELF_CHECK
      }
      //assert(dest(searchtet) == pt[0]); // SELF_CHECK
      // Only connect: tmpsh<--searchtet. So stpivot() works.
      sesymself(tmpsh); 
      tmpsh.sh[6 + EdgeRing(tmpsh.shver)] = (shellface) encode(searchtet);
    } else {
      if (b->verbose > 1) {
        //printf("  p:draw_subface(%d, %d, %d) -- %d is missing\n",
          //pointmark(pt[0]), pointmark(pt[1]), pointmark(pt[2]), i);
      }
      shellfacedealloc(subfaces, tmpsh.sh);
      // Save this face in list.
      misfaces->newindex((void **) &parytet1);
      *parytet1 = *parytet;
      continue;
    }
    // Remember the boundary tet in tmpsh (use the adjacent subface slot). 
    tmpsh.sh[0] = (shellface) encode(*parytet);
    // Save this subface.
    cavshells->newindex((void **) &parysh);
    *parysh = tmpsh;
  }

  if (misfaces->objects > 0) {
    // Removing tempoaray subfaces.
    for (i = 0; i < (int) cavshells->objects; i++) {
      parysh = (face *) fastlookup(cavshells, i);
      stpivot(*parysh, neightet);
      tsdissolve(neightet); // Detach it from adj. tets.
      symself(neightet);
      tsdissolve(neightet);
      shellfacedealloc(subfaces, parysh->sh);
    }
    cavshells->restart();

    // Infect the points which are of the cavity for detecting new 
    //   cavity point due to the enlargement.
    for (i = 0; i < (int) cavpoints->objects; i++) {
      pt[0] = * (point *) fastlookup(cavpoints, i);
      pinfect(pt[0]); // Mark it as inserted.
    }

    // Enlarge the cavity.
    for (i = 0; i < (int) misfaces->objects; i++) {
      // Get a missing face.
      parytet = (triface *) fastlookup(misfaces, i);
      if (!infected(*parytet)) {
        if (oppo(*parytet) == dummypoint) {
          //printf("Internal error:  A convex hull is missing.\n");
          terminatetetgen(2);
        }
        // Put it into crossing tet list.
        infect(*parytet);
        crosstets->newindex((void **) &parytet1);
        *parytet1 = *parytet;
        // Insert the opposite point if it is not in DT.
        pd = oppo(*parytet);
        if (!pinfected(pd)) {
          if (b->verbose > 1) {
            //printf("    Insert the opposite point %d.\n", pointmark(pd));
          }
          pinfect(pd);
          cavpoints->newindex((void **) &parypt);
          *parypt = pd;
          searchtet = recenttet;
          insertvertexbw(pd, &searchtet, true, false, false, false);
        }
        // Check for a missing subface.
        tspivot(*parytet, checksh);
        if (checksh.sh != dummysh) {
          if (b->verbose > 1) {
            //printf("    Queue a subface x%lx (%d, %d, %d).\n", 
              //(unsigned long) checksh.sh, pointmark(sorg(checksh)),
              //pointmark(sdest(checksh)), pointmark(sapex(checksh)));
          }
          stdissolve(checksh);
          sesymself(checksh);
          stdissolve(checksh);
          subfacstack->newindex((void **) &parysh);
          *parysh = checksh;
        }
        // Add three opposite faces into the boundary list.
        for (j = 0; j < 3; j++) {
          fnext(*parytet, spintet);
          symedge(spintet, neightet);
          if ((neightet.tet == dummytet) || !infected(neightet)) {
            if (b->verbose > 1) {
              //printf("    Add a cavface (%d, %d, %d).\n",
                //pointmark(org(spintet)), pointmark(dest(spintet)),
               // pointmark(apex(spintet)));
            }
            cavfaces->newindex((void **) &parytet1);
            if (neightet.tet == dummytet) {
              maketetrahedron(&faketet);  // Create a faked tet.
              setorg(faketet, org(spintet));
              setdest(faketet, dest(spintet));
              setapex(faketet, apex(spintet));
              setoppo(faketet, dummypoint);
              bond(spintet, faketet);
              tspivot(spintet, checksh);
              if (checksh.sh != dummysh) {
                sesymself(checksh);
                tspivot(faketet, checksh);
              }
              for (k = 0; k < 3; k++) {
                tsspivot1(spintet, checkseg);
                if (checkseg.sh != dummysh) {
                  tssbond1(faketet, checkseg);
                }
                enextself(spintet);
                enextself(faketet);
              }
              *parytet1 = faketet;
            } else {
              *parytet1 = neightet;
            }
          } else {
            // Check if a subface is missing again.
            tspivot(neightet, checksh);
            if (checksh.sh != dummysh) {
              if (b->verbose > 1) {
                //printf("    Queue a subface x%lx (%d, %d, %d).\n", 
                  //(unsigned long) checksh.sh, pointmark(sorg(checksh)),
                  //pointmark(sdest(checksh)), pointmark(sapex(checksh)));
              }
              stdissolve(checksh);
              sesymself(checksh);
              stdissolve(checksh);
              subfacstack->newindex((void **) &parysh);
              *parysh = checksh;
            }
          }
          enextself(*parytet);
        } // j
      } // if (!infected(parytet))
    }

    // Uninfect the points which are of the cavity.
    for (i = 0; i < (int) cavpoints->objects; i++) {
      pt[0] = * (point *) fastlookup(cavpoints, i);
      puninfect(pt[0]);
    }

    misfaces->restart();
    cavityexpcount++;
    continue;
  }

  break;

  } // while (1)

  // Collect all tets of the DT. All new tets are marktested.
  marktest(recenttet);
  newtets->newindex((void **) &parytet);
  *parytet = recenttet;
  for (i = 0; i < (int) newtets->objects; i++) {
    searchtet = * (triface *) fastlookup(newtets, i);
    for (searchtet.loc = 0; searchtet.loc < 4; searchtet.loc++) {
      sym(searchtet, neightet);
      if (neightet.tet != dummytet) {
        if (!marktested(neightet)) {
          marktest(neightet);
          newtets->newindex((void **) &parytet);
          *parytet = neightet;
        }
      }
    }
  }

  cavpoints->restart();
  // Comment: Now no vertex is marked.
  cavfaces->restart();

  if (cavshells->objects > (long) maxcavsize) {
    maxcavsize = cavshells->objects;
  }

  return true;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// fillcavity()    Fill new tets into the cavity.                            //
//                                                                           //
// The new tets are stored in two disjoint sets(which share the same facet). //
// 'topfaces' and 'botfaces' are the boundaries of these two sets, respect-  //
// ively. 'midfaces' is empty on input, and will store faces in the facet.   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::fillcavity(arraypool* topshells, arraypool* botshells,
  arraypool* midfaces, arraypool* facpoints)
{
  arraypool *cavshells;
  triface *parytet, bdrytet, toptet, bottet, neightet, midface, spintet;
  face checksh, *parysh;
  face checkseg;
  point pa, pb, pc, pf, pg;
  REAL ori, len, n[3];
  bool mflag, bflag;
  int i, j, k;

  // Connect newtets to tets outside the cavity.
  for (k = 0; k < 2; k++) {
    cavshells = (k == 0 ? topshells : botshells);
    if (cavshells != NULL) {
      for (i = 0; i < (int) cavshells->objects; i++) {
        // Get a temp subface.
        parysh = (face *) fastlookup(cavshells, i);
        // Get the boundary tet outsode the cavity.
        decode(parysh->sh[0], bdrytet);
        pa = sorg(*parysh);
        pb = sdest(*parysh);
        // Fix bdrytet at the edge pb->pa.
        bdrytet.ver = 0;
        for (j = 0; j < 3; j++) {
          if (org(bdrytet) == pb) break;
          enextself(bdrytet); 
        }
        //assert(j < 3);
        //assert(dest(bdrytet) == pa);
        // pa = org(bdrytet);
        // pb = dest(bdrytet);
        pc = apex(bdrytet);
        // Get the adjacent new tet which is in the cavity.
        stpivot(*parysh, neightet);
        // Fix neightet at the edge pa->pb.
        neightet.ver = 0;
        for (j = 0; j < 3; j++) {
          if (org(neightet) == pa) break;
          enextself(neightet); 
        }
        //assert(j < 3);
        //assert(dest(neightet) == pb); // SELF_CHECK
        // Mark neightet as an interior tet of this cavity, 2009-04-24.
        if (!infected(neightet)) {
          infect(neightet);
        }
        // Comment: bdrytet may be a faked tet, Bond it if it is not 
        //   marktested, i.e., it is not created for holding an interor
        //   subface.  The connections will be used in fillcavity for
        //   finding middle faces.
        if (!marktested(bdrytet)) {
          // Bond the two tets.
          bond(bdrytet, neightet);
        // } else {
          // A new boundary face.
          // dummytet[0] = encode(neightet);
        }
        // Bond a subface (if it exists).
        tspivot(bdrytet, checksh);
        if (checksh.sh != dummysh) {
          sesymself(checksh);
          tsbond(neightet, checksh); // Also cleared the pointer to tmpsh.
        } else {
          tsdissolve(neightet); // No subface, clear the pointer to tmpsh.
        }
        // Bond subsegments
        for (j = 0; j < 3; j++) {
          tsspivot1(bdrytet, checkseg);
          if (checkseg.sh != dummysh) {
            spintet = neightet;
            while (1) {
              tssbond1(spintet, checkseg);
              tfnextself(spintet);
              if (spintet.tet == dummytet) break; // Outside the cavity.
              if (!marktested(spintet)) break; // Outside the cavity.
              if (spintet.tet == neightet.tet) break; // Turn back.
            }
          }
          enextself(bdrytet);
          enext2self(neightet);
        }
        // Update the point-to-tets map.
        setpoint2tet(pa, encode(neightet));
        setpoint2tet(pb, encode(neightet));
        setpoint2tet(pc, encode(neightet));
        // Delete the temp subface.
        // shellfacedealloc(subfacepool, parysh->sh);
        // if (oppo(bdrytet) == dummypoint) {
          // Delete a faked tet.
          // tetrahedrondealloc(bdrytet.tet);
        // }
      }
    } // if (cavshells != NULL)
  }

  mflag = true;  // Initialize it.

  if (midfaces != NULL) {

  // Mark all facet vertices for finding middle subfaces.
  for (i = 0; i < (int) facpoints->objects; i++) {
    pf = * (point *) fastlookup(facpoints, i);
    pinfect(pf);
  }

  // The first pair of top and bottom tets share the same edge [a, b].
  // toptet = * (triface *) fastlookup(topfaces, 0);
  if (infected(firsttopface)) {
    // The cavity was enlarged. This tet is included in the interior
    //   (as those of a crossing tet). Find the updated top boundary face
    //   by rotating the faces around this edge (until an uninfect tet).
    pa = apex(firsttopface);
    while (1) {
      tfnextself(firsttopface);
      //assert(firsttopface.tet != dummytet);
      if (!infected(firsttopface)) break;
      //assert(apex(firsttopface) != pa); // SELF_CHECK
    }
  }
  toptet = firsttopface;
  symedgeself(toptet);
  //assert(marktested(toptet)); // It must be a new tet.
  // Search a subface from the top mesh.
  while (1) {
    fnextself(toptet); // The next face in the same tet.
    pc = apex(toptet);
    if (pinfected(pc)) break; // [a,b,c] is a subface.
    symedgeself(toptet); // Go to the same face in the adjacent tet.
    //assert(toptet.tet != dummytet);
  }
  // Search the subface [a,b,c] in the bottom mesh.
  // bottet = * (triface *) fastlookup(botfaces, 0);
  if (infected(firstbotface)) {
    pa = apex(firstbotface);
    while (1) {
      tfnextself(firstbotface);
      //assert(firstbotface.tet != dummytet);
      if (!infected(firstbotface)) break;
      //assert(apex(firstbotface) != pa); // SELF_CHECK
    }
  }
  bottet = firstbotface;
  symedgeself(bottet);
  //assert(marktested(bottet)); // It must be a new tet.
  while (1) {
    fnextself(bottet); // The next face in the same tet.
    pf = apex(bottet);
    if (pf == pc) break; // Face matched.
    if (pinfected(pf)) {
      mflag = false; break; // Not matched.
    }
    symedgeself(bottet);
    //assert(bottet.tet != dummytet);
  }
  if (mflag) {
    // Connect the two tets together.
    bond(toptet, bottet);
    // Both are interior tets.
    infect(toptet);
    infect(bottet);
    // Add this face into search list.
    // esymself(toptet); // Choose the 0th edge ring.
    markface(toptet);
    midfaces->newindex((void **) &parytet);
    *parytet = toptet;
  }

  // Match pairs of subfaces (middle faces), connect top and bottom tets.
  for (i = 0; i < (int) midfaces->objects && mflag; i++) {
    // Get a matched middle face [a, b, c]
    midface = * (triface *) fastlookup(midfaces, i);
    // It is inside the cavity.
    //assert(marktested(midface)); // SELF_CHECK 
    // Check the neighbors at edges [b, c] and [c, a].
    midface.ver = 0;
    for (j = 0; j < 3 && mflag; j++) {
      pg = apex(midface);
      toptet = midface;
      bflag = false;
      while (1) {
        // Go to the next face in the same tet.
        fnextself(toptet);
        pc = apex(toptet);
        if (pinfected(pc)) {
          break; // Find a subface.
        }
        // if (pc == dummypoint) {
        //   break; // Find a subface.
        // }
        /* if (pc == pg) {
          // The adjacent face is not a middle face.
          bflag = true; break; 
        }*/
        symedgeself(toptet);
        //assert(toptet.tet != dummytet); // The adjacent tet must exist.
        // Do we walk outside the cavity?
        if (!marktested(toptet)) {
          // Yes, the adjacent face is not a middle face.
          bflag = true; break; 
        }
      }
      if (!bflag) {
        // //assert(marktested(toptet)); // SELF_CHECK
        if (!facemarked(toptet)) {
          symedge(midface, bottet);
          while (1) {
            fnextself(bottet);
            pf = apex(bottet);
            if (pf == pc) break; // Face matched.
            if (pinfected(pf)) {
              mflag = false; break; // Not matched.
            }
            symedgeself(bottet);
            //assert(bottet.tet != dummytet); // The adjacent tet must exist.
          }
          if (mflag) {
            if (marktested(bottet)) {
              // Connect two tets together.
              bond(toptet, bottet);
              // Both are interior tets.
              infect(toptet);
              infect(bottet);
              // Add this face into list.
              // esymself(toptet);
              markface(toptet);
              midfaces->newindex((void **) &parytet);
              *parytet = toptet;
            } else {
              // The 'bottet' is not inside the cavity! 
              // This case can happen when the cavity was enlarged, and the
              //   'toptet' is a co-facet (sub)face adjacent to the missing
              //   region, and it is a boundary face of the top cavity.
              // So the toptet and bottet should be bonded already through
              //   a temp subface. See fig/dump-cavity-case18. Check it.
              symedge(toptet, neightet);
              //assert(neightet.tet == bottet.tet); // SELF_CHECK
              //assert(neightet.loc == bottet.loc); // SELF_CHECK
              // Do not add this face into 'midfaces'.
            }
          }
        }
      }
      enextself(midface); // Go to the next edge.
    } // j
  } // i

  } // if (midfaces != NULL)

  if (mflag) {
    if (midfaces != NULL) {
      if (b->verbose > 1) {
        //printf("    Found %ld middle subfaces.\n", midfaces->objects);
      }
      if (midfaces->objects > (long) maxregionsize) {
        maxregionsize = (long) midfaces->objects;
      }
      // Unmark middle faces.
      for (i = 0; i < (int) midfaces->objects; i++) {
        // Get a matched middle face [a, b, c]
        midface = * (triface *) fastlookup(midfaces, i);
        //assert(facemarked(midface)); // SELF_CHECK
        unmarkface(midface);
      }
    }
  } else {
    // Faces at top and bottom are not matched. There exists non-Delaunay
    //   subedges. See fig/dump-cavity-case5.lua. 
    pa = org(toptet);
    pb = dest(toptet);
    pc = apex(toptet);
    pf = apex(bottet);
    if (0) { // if (b->verbose > 1) {
      //printf("  p:draw_tet(%d, %d, %d, %d) -- top tet.\n", pointmark(pa), 
        //pointmark(pb), pointmark(pc), pointmark(oppo(toptet)));
      //printf("  p:draw_tet(%d, %d, %d, %d) -- bot tet.\n", 
       // pointmark(org(bottet)), pointmark(dest(bottet)), 
       // pointmark(apex(bottet)), pointmark(oppo(bottet)));
    }
    // Calculate a point above the faces.
    facenormal2(pa, pb, pc, n, 1);
    len = sqrt(DOT(n, n));
    n[0] /= len;
    n[1] /= len;
    n[2] /= len;
    len = DIST(pa, pb);
    len += DIST(pb, pc);
    len += DIST(pc, pa);
    len /= 3.0;
    dummypoint[0] = pa[0] + len * n[0];
    dummypoint[1] = pa[1] + len * n[1];
    dummypoint[2] = pa[2] + len * n[2];
    // Find the crossing edges.
    ori = orient3d(pb, pc, dummypoint, pf);
    //assert(ori != 0); // SELF_CHECK
    if (ori < 0) {
      // The top edge [b, c] intersects the bot edge [a, f].
      enextself(toptet); 
      enextself(bottet);
    } else {
      // The top edge [c, a] intersects the bot edge [f, b].
      enext2self(toptet); 
      enext2self(bottet); 
    }
    // Split one of the edges, choose the one has longer length.
    n[0] = DIST(org(toptet), dest(toptet));
    n[1] = DIST(org(bottet), dest(bottet));
    if (n[0] > n[1]) {
      pf = org(toptet);
      pg = dest(toptet);
    } else {
      pf = org(bottet);
      pg = dest(bottet);
    }
    if (b->verbose > 1) {
      //printf("  Found a non-Delaunay edge (%d, %d)\n", pointmark(pf), 
        //pointmark(pg));
    }
    // Create the midpoint of the non-Delaunay edge.
    for (i = 0; i < 3; i++) {
      dummypoint[i] = 0.5 * (pf[i] + pg[i]);
    }
    // Set a tet for searching the new point.
    recenttet = firsttopface;
    // dummypoint[0] = dummypoint[1] = dummypoint[2] = 0;
    ndelaunayedgecount++;
  }

  if (facpoints != NULL) {
    // Unmark all facet vertices.
    for (i = 0; i < (int) facpoints->objects; i++) {
      pf = * (point *) fastlookup(facpoints, i);
      puninfect(pf);
    }
  }
  
  // Delete the temp subfaces and faked tets.
  for (k = 0; k < 2; k++) {
    cavshells = (k == 0 ? topshells : botshells);
    if (cavshells != NULL) {
      for (i = 0; i < (int) cavshells->objects; i++) {
        parysh = (face *) fastlookup(cavshells, i);
        decode(parysh->sh[0], bdrytet);
        if (oppo(bdrytet) == dummypoint) {
          sym(bdrytet, neightet);
          if (neightet.tet != dummytet) {
            // This side is a hull face (not an interior subface).
            dissolve(neightet);
            dummytet[0] = encode(neightet);
            tspivot(neightet, checksh);
            if (checksh.sh != dummysh) {
              //assert(checksh.sh != parysh->sh);
              // Dis-coonection tet-subface bond.
              sesymself(checksh);
              stdissolve(checksh);
            }
          }
          // Delete a faked tet.
          tetrahedrondealloc(bdrytet.tet);
        }
        shellfacedealloc(subfaces, parysh->sh);
      }
    }
  }

  topshells->restart();
  if (botshells != NULL) {
    botshells->restart();
  }
  if (midfaces != NULL) {
    midfaces->restart();
  }
  // Comment: Now no vertex is marked.

  return mflag;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// carvecavity()    Delete old tets and outer new tets of the cavity.        //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::carvecavity(arraypool *crosstets, arraypool *topnewtets,
  arraypool *botnewtets)
{
  arraypool *newtets;
  triface *parytet, *pnewtet, neightet;
  face checkseg; //, *parysh;
  // int hitbdry;
  int i, j, k;

  /*// NOTE: Some subsegments may contained inside the cavity. They must be
  //   queued for recovery. See fig/dump-cavity-case20.
  for (i = 0; i < (int) crosstets->objects; i++) {
    parytet = (triface *) fastlookup(crosstets, i);
    //assert(infected(*parytet)); // SELF_CHECK
    if (parytet->tet[8] != NULL) {
      for (j = 0; j < 6; j++) {
        parytet->loc = edge2locver[j][0];
        parytet->ver = edge2locver[j][1];
        tsspivot1(*parytet, checkseg);
        if (checkseg.sh != dummysh) {
          if (!sinfected(checkseg)) {
            // It is not queued yet.
            neightet = *parytet;
            hitbdry = 0;
            while (1) {
              tfnextself(neightet);
              if (neightet.tet == dummytet) {
                hitbdry++;
                if (hitbdry == 2) break;
                esym(*parytet, neightet);
                tfnextself(neightet);
                if (neightet.tet == dummytet) break;
              }
              if (!infected(neightet)) break;
              if (apex(neightet) == apex(*parytet)) break;
            }
            if (infected(neightet)) {
              if (b->verbose > 1) {
                //printf("    Queue a missing segment (%d, %d).\n",
                  pointmark(sorg(checkseg)), pointmark(sdest(checkseg)));
              }
              sinfect(checkseg);
              subsegstack->newindex((void **) &parysh);
              *parysh = checkseg;
            }
          }
        }
      }
    }
  }*/

  // Delete the old tets in cavity.
  for (i = 0; i < (int) crosstets->objects; i++) {
    parytet = (triface *) fastlookup(crosstets, i);
    tetrahedrondealloc(parytet->tet);
  }
  crosstets->restart(); // crosstets will be re-used.

  // Collect infected new tets in cavity.
  for (k = 0; k < 2; k++) {
    newtets = (k == 0 ? topnewtets : botnewtets);
    if (newtets != NULL) {
      for (i = 0; i < (int) newtets->objects; i++) {
        parytet = (triface *) fastlookup(newtets, i);
        if (infected(*parytet)) {
          crosstets->newindex((void **) &pnewtet);
          *pnewtet = *parytet;
        }
      }
    }
  }
  // Collect all new tets in cavity.
  for (i = 0; i < (int) crosstets->objects; i++) {
    parytet = (triface *) fastlookup(crosstets, i);
    if (i == 0) {
      recenttet = *parytet; // Remember a live handle.
    }
    for (j = 0; j < 4; j++) {
      decode(parytet->tet[j], neightet);
      if (marktested(neightet)) { // Is it a new tet?
        if (!infected(neightet)) {
          // Find an interior tet.
          //assert(neightet.tet != dummytet); // SELF_CHECK
          infect(neightet);
          crosstets->newindex((void **) &pnewtet);
          *pnewtet = neightet;
        }
      }
    }
  }

  // Delete outer new tets (those new tets which are not infected).
  for (k = 0; k < 2; k++) {
    newtets = (k == 0 ? topnewtets : botnewtets);
    if (newtets != NULL) {
      for (i = 0; i < (int) newtets->objects; i++) {
        parytet = (triface *) fastlookup(newtets, i);
        if (infected(*parytet)) {
          // This is an interior tet.
          uninfect(*parytet);
          unmarktest(*parytet);
        } else {
          // An outer tet. Delete it.
          tetrahedrondealloc(parytet->tet);
        }
      }
    }
  }

  crosstets->restart();
  topnewtets->restart();
  if (botnewtets != NULL) {
    botnewtets->restart();
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// restorecavity()    Reconnect old tets and delete new tets of the cavity.  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::restorecavity(arraypool *crosstets, arraypool *topnewtets,
  arraypool *botnewtets)
{
  triface *parytet, neightet;
  face checksh;
  point *ppt;
  int i, j;

  // Reconnect crossing tets to cavity boundary.
  for (i = 0; i < (int) crosstets->objects; i++) {
    parytet = (triface *) fastlookup(crosstets, i);
    //assert(infected(*parytet)); // SELF_CHECK
    if (i == 0) {
      recenttet = *parytet; // Remember a live handle.
    }
    parytet->ver = 0;
    for (parytet->loc = 0; parytet->loc < 4; parytet->loc++) {
      sym(*parytet, neightet);
      // The neighbor may be a deleted faked tet.
      if (isdead(&neightet) || (neightet.tet == dummytet)) {
        dissolve(*parytet);  // Detach a faked tet.
        // Remember a boundary tet.
        dummytet[0] = encode(*parytet);
      } else if (!infected(neightet)) {
        bond(*parytet, neightet);
        tspivot(*parytet, checksh);
        if (checksh.sh != dummysh) {
          tsbond(*parytet, checksh);
        }
      }
    }
    // Update the point-to-tet map.
    parytet->loc = 0;
    ppt = (point *) &(parytet->tet[4]);
    for (j = 0; j < 4; j++) {
      setpoint2tet(ppt[j], encode(*parytet));
    }
  }

  // Uninfect all crossing tets.
  for (i = 0; i < (int) crosstets->objects; i++) {
    parytet = (triface *) fastlookup(crosstets, i);
    uninfect(*parytet);
  }

  // Delete new tets.
  for (i = 0; i < (int) topnewtets->objects; i++) {
    parytet = (triface *) fastlookup(topnewtets, i);
    tetrahedrondealloc(parytet->tet);
  }

  if (botnewtets != NULL) {
    for (i = 0; i < (int) botnewtets->objects; i++) {
      parytet = (triface *) fastlookup(botnewtets, i);
      tetrahedrondealloc(parytet->tet);
    }
  }

  crosstets->restart();
  topnewtets->restart();
  if (botnewtets != NULL) {
    botnewtets->restart();
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// splitsubedge()    Split a non-Delaunay edge (not a segment) in the        //
//                   surface mesh of a facet.                                //
//                                                                           //
// The new point 'newpt' will be inserted in the tetrahedral mesh if it does //
// not cause any existing (sub)segments become non-Delaunay.  Otherwise, the //
// new point is not inserted and one of such subsegments will be split.      //
//                                                                           //
// Next,the actual inserted new point is also inserted into the surface mesh.//
// Non-Delaunay segments and newly created subfaces are queued for recovery. //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::splitsubedge(point newpt, face *searchsh, arraypool *facfaces,
  arraypool *facpoints)
{
  // queue *flipqueue;
  triface searchtet;
  face splitsh;
  face *psseg, sseg; // *parysh;
  point pa, pb;
  enum locateresult loc;
  int s, i;

  // Try to insert the point. Do not insert if it will encroach any segment
  //   (noencsegflag is TRUE). Queue encroacged subfaces.
  //assert(subsegstack->objects == 0l); // SELF_CHECK
  searchtet = recenttet; // Start search it from recentet
  // loc = insertvertexbw(newpt, &searchtet, true, true, true, false);
  // Always insert this point, missing segments are queued. 2009-06-11.
  loc = insertvertexbw(newpt, &searchtet, true, true, false, false);

  if (loc == ENCSEGMENT) {
    // Some segments are encroached. Randomly pick one to split.
    //assert(subsegstack->objects > 0l);
    s = randomnation(subsegstack->objects);
    psseg = (face *) fastlookup(subsegstack, s);
    sseg = *psseg;
    pa = sorg(sseg);
    pb = sdest(sseg);
    for (i = 0; i < 3; i++) newpt[i] = 0.5 * (pa[i] + pb[i]);
    setpointtype(newpt, FREESEGVERTEX);
    setpoint2sh(newpt, sencode(sseg));  
    // Uninfect all queued segments.
    for (i = 0; i < (int) subsegstack->objects; i++) {
      psseg = (face *) fastlookup(subsegstack, i);
      suninfect(*psseg);
    }
    subsegstack->restart();  // Clear the queue.
    // Split the segment. Two subsegments are queued.
    sinsertvertex(newpt, searchsh, &sseg, true, false);
    // Insert the point. Missing segments are queued. 
    searchtet = recenttet; // Start search it from recentet
    insertvertexbw(newpt, &searchtet, true, true, false, false);
  } else {
    /*// Calc an above point for point location in surface triangulation.
    calculateabovepoint(facpoints, NULL, NULL, NULL); 
    // Insert the new point on facet. New subfaces are queued for reocvery.
    loc = sinsertvertex(newpt, searchsh, NULL, true, false);
    if (loc == OUTSIDE) {
      //assert(0); // Not handled yet.
    }
    // Clear the above point.
    dummypoint[0] = dummypoint[1] = dummypoint[2] = 0;
    */
    // Set the abovepoint of f for point location.
    abovepoint = facetabovepointarray[shellmark(*searchsh)];
    if (abovepoint == (point) NULL) {
      getfacetabovepoint(searchsh);
    }
    // Insert the new point on facet. New subfaces are queued for reocvery.
    loc = sinsertvertex(newpt, searchsh, NULL, true, false);
    if (loc == OUTSIDE) {
      //assert(0); // Not handled yet.
    }
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// constrainedfacets()    Recover subfaces saved in 'subfacestack'.          //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::constrainedfacets2()
{
  arraypool *crosstets, *topnewtets, *botnewtets;
  arraypool *topfaces, *botfaces, *midfaces;
  arraypool *topshells, *botshells, *facfaces;
  arraypool *toppoints, *botpoints, *facpoints;
  triface *parytet, searchtet, neightet;
  face *pssub, ssub, neighsh;
  face checkseg;
  point *ppt, pt, newpt;
  enum interresult dir;
  bool success, delaunayflag;
  long bakflip22count;
  long cavitycount;
  int facetcount;
  int bakhullsize;
  int s, i, j;

  if (b->verbose) {
    //printf("  Constraining facets.\n");
  }

  // Initialize arrays.
  crosstets = new arraypool(sizeof(triface), 10);
  topnewtets = new arraypool(sizeof(triface), 10);
  botnewtets = new arraypool(sizeof(triface), 10);
  topfaces = new arraypool(sizeof(triface), 10);
  botfaces = new arraypool(sizeof(triface), 10);
  midfaces = new arraypool(sizeof(triface), 10);
  toppoints = new arraypool(sizeof(point), 8);
  botpoints = new arraypool(sizeof(point), 8);
  facpoints = new arraypool(sizeof(point), 8);
  facfaces = new arraypool(sizeof(face), 10);
  topshells = new arraypool(sizeof(face), 10);
  botshells = new arraypool(sizeof(face), 10);

  bakflip22count = flip22count;
  cavitycount = 0;
  facetcount = 0;

  // Loop until 'subfacstack' is empty.
  while (subfacstack->objects > 0l) {
    subfacstack->objects--;
    pssub = (face *) fastlookup(subfacstack, subfacstack->objects);
    ssub = *pssub;

    if (ssub.sh[3] == NULL) continue; // Skip a dead subface.

    stpivot(ssub, neightet);
    if (neightet.tet == dummytet) {
      sesymself(ssub);
      stpivot(ssub, neightet);
    }

    if (neightet.tet == dummytet) {
      // Find an unrecovered subface.
      smarktest(ssub);
      facfaces->newindex((void **) &pssub);
      *pssub = ssub;
      // Get all subfaces and vertices of the same facet.
      for (i = 0; i < (int) facfaces->objects; i++) {
        ssub = * (face *) fastlookup(facfaces, i);
        for (j = 0; j < 3; j++) {
          sspivot(ssub, checkseg);
          if (checkseg.sh == dummysh) {
            spivot(ssub, neighsh);
            //assert(neighsh.sh != dummysh); // SELF_CHECK
            if (!smarktested(neighsh)) {
              // It may be already recovered.
              stpivot(neighsh, neightet);
              if (neightet.tet == dummytet) {
                sesymself(neighsh);
                stpivot(neighsh, neightet);
              }
              if (neightet.tet == dummytet) {
                // Add it into list.
                smarktest(neighsh);
                facfaces->newindex((void **) &pssub);
                *pssub = neighsh;
              }
            }
          }
          pt = sorg(ssub);
          if (!pinfected(pt)) {
            pinfect(pt);
            facpoints->newindex((void **) &ppt);
            *ppt = pt;
          }
          senextself(ssub);
        } // j
      } // i
      // Have found all facet subfaces (vertices). Uninfect them.
      for (i = 0; i < (int) facfaces->objects; i++) {
        pssub = (face *) fastlookup(facfaces, i);
        sunmarktest(*pssub);
      }
      for (i = 0; i < (int) facpoints->objects; i++) {
        ppt = (point *) fastlookup(facpoints, i);
        puninfect(*ppt);
      }
      if (b->verbose > 1) {
        //printf("  Recover facet #%d: %ld subfaces, %ld vertices.\n", 
          //facetcount + 1, facfaces->objects, facpoints->objects);
      }
      facetcount++;

      // Loop until 'facfaces' is empty.
      while (facfaces->objects > 0l) {
        // Get the last subface of this array.
        facfaces->objects--;
        pssub = (face *) fastlookup(facfaces, facfaces->objects);
        ssub = *pssub;

        stpivot(ssub, neightet);
        if (neightet.tet == dummytet) {
          sesymself(ssub);
          stpivot(ssub, neightet);        
        }

        if (neightet.tet != dummytet) continue; // Not a missing subface.

        // Insert the subface.
        searchtet.tet = NULL;
        dir = scoutsubface(&ssub, &searchtet, 1);
        if (dir == SHAREFACE) continue; // The subface is inserted.
        //assert(dir != COLLISIONFACE); // SELF_CHECK

        // Not exist. Push the subface back into stack.
        s = randomnation(facfaces->objects + 1);
        facfaces->newindex((void **) &pssub);
        *pssub = * (face *) fastlookup(facfaces, s);
        * (face *) fastlookup(facfaces, s) = ssub;

        if (dir == EDGETRIINT) continue; // All three edges are missing.

        // Search for a crossing tet.
        dir = scoutcrosstet(&ssub, &searchtet, facpoints);

        if (dir == INTERTET) {
          // Recover subfaces by local retetrahedralization.
          cavitycount++;
          bakhullsize = hullsize;
          checksubsegs = checksubfaces = 0;
          crosstets->newindex((void **) &parytet);
          *parytet = searchtet;
          // Form a cavity of crossing tets.
          formcavity(&ssub, crosstets, topfaces, botfaces, toppoints,
            botpoints, facpoints, facfaces);
          delaunayflag = true;
          // Tetrahedralize the top part. Re-use 'midfaces'.
          success = delaunizecavity(toppoints, topfaces, topshells,
            topnewtets, crosstets, midfaces);
          if (success) {
            // Tetrahedralize the bottom part. Re-use 'midfaces'.
            success = delaunizecavity(botpoints, botfaces, botshells, 
              botnewtets, crosstets, midfaces);
            if (success) {
              // Fill the cavity with new tets.
              success = fillcavity(topshells, botshells, midfaces, facpoints);
              if (success) {
                // Delete old tets and outer new tets.
                carvecavity(crosstets, topnewtets, botnewtets);
              }
            } else {
              delaunayflag = false;
            }
          } else {
            delaunayflag = false;
          }
          if (!success) {
            // Restore old tets and delete new tets.
            restorecavity(crosstets, topnewtets, botnewtets);
          }
          /*if (!delaunayflag) {
            dump_facetof(&ssub, "facet1.lua");
            while (futureflip != NULL) {
              formedgecavity(futureflip->forg, futureflip->fdest, crosstets, 
                topfaces, toppoints);
              crosstets->restart();
              topfaces->restart();
              toppoints->restart();
              futureflip = futureflip->nextitem;
            }
            flippool->restart();
            outnodes(0);
            checkmesh();
            checkshells(1);
            //assert(0); // Stop the program.
          }*/
          hullsize = bakhullsize;
          checksubsegs = checksubfaces = 1;
        } else if (dir == INTERFACE) {
          // Recover subfaces by flipping edges in surface mesh.
          recoversubfacebyflips(&ssub, &searchtet, facfaces);
          success = true;
        } else { // dir == TOUCHFACE
          //assert(0);
        }
        if (!success) break;
      } // while

      if (facfaces->objects > 0l) {
        // Found a non-Delaunay edge, split it (or a segment close to it).
        // Create a new point at the middle of this edge, its coordinates
        //   were saved in dummypoint in 'fillcavity()'.
        makepoint(&newpt);
        for (i = 0; i < 3; i++) newpt[i] = dummypoint[i];
        setpointtype(newpt, FREESUBVERTEX);
        setpoint2sh(newpt, sencode(ssub));  
        dummypoint[0] = dummypoint[1] = dummypoint[2] = 0;
        // Insert the new point. Starting search it from 'ssub'.
        splitsubedge(newpt, &ssub, facfaces, facpoints);
        facfaces->restart();
      }
      // Clear the list of facet vertices.
      facpoints->restart();

      // Some subsegments may be queued, recover them.
      if (subsegstack->objects > 0l) {
        b->verbose--; // Suppress the message output.
        delaunizesegments2();
        b->verbose++;
      }
      // Now the mesh should be constrained Delaunay.
    } // if (neightet.tet == NULL) 
  }

  if (b->verbose) {
    //printf("  %ld subedge flips.\n", flip22count - bakflip22count);
    //printf("  %ld cavities remeshed.\n", cavitycount);
  }

  // Delete arrays.
  delete crosstets;
  delete topnewtets;
  delete botnewtets;
  delete topfaces;
  delete botfaces;
  delete midfaces;
  delete toppoints;
  delete botpoints;
  delete facpoints;
  delete facfaces;
  delete topshells;
  delete botshells;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// formskeleton()    Form a constrained tetrahedralization.                  //
//                                                                           //
// The segments and facets of a PLS will be recovered.                       //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::formskeleton(clock_t& tv)
{
  triface searchtet;
  face *pssub, ssub;
  int s, i;

  if (!b->quiet) {
    //printf("Recovering boundaries.\n");
  }

  caveshlist = new arraypool(sizeof(face), 10);
  caveshbdlist = new arraypool(sizeof(face), 10);

  // Put all segments into the list.
  if (b->nojettison == 1) {  // '-J' option (for debug)
    // The sequential order.
    subsegs->traversalinit();
    for (i = 0; i < subsegs->items; i++) {
      ssub.sh = shellfacetraverse(subsegs);
      sinfect(ssub);  // Only save it once.
      subsegstack->newindex((void **) &pssub);
      *pssub = ssub;
    }
  } else {
    // Randomly order the segments.
    subsegs->traversalinit();
    for (i = 0; i < subsegs->items; i++) {
      s = randomnation(i + 1);
      // Move the s-th seg to the i-th.
      subsegstack->newindex((void **) &pssub);
      *pssub = * (face *) fastlookup(subsegstack, s);
      // Put i-th seg to be the s-th.
      ssub.sh = shellfacetraverse(subsegs);
      sinfect(ssub);  // Only save it once.
      pssub = (face *) fastlookup(subsegstack, s);
      *pssub = ssub;
    }
  }

  // Segments will be introduced.
  checksubsegs = 1;
  // Recover segments.
  delaunizesegments2();

  tv = clock();

  // Randomly order the subfaces.
  subfaces->traversalinit();
  for (i = 0; i < subfaces->items; i++) {
    s = randomnation(i + 1);
    // Move the s-th subface to the i-th.
    subfacstack->newindex((void **) &pssub);
    *pssub = * (face *) fastlookup(subfacstack, s);
    // Put i-th subface to be the s-th.
    ssub.sh = shellfacetraverse(subfaces);
    pssub = (face *) fastlookup(subfacstack, s);
    *pssub = ssub;
  }

  // Subfaces will be introduced.
  checksubfaces = 1;
  // Recover facets.
  constrainedfacets2();

  delete caveshlist;
  delete caveshbdlist;
  caveshlist = NULL;
  caveshbdlist = NULL;

  // Detach all segments from tets.
  tetrahedrons->traversalinit();
  searchtet.tet = tetrahedrontraverse();
  while (searchtet.tet != (tetrahedron *) NULL) {
    if (searchtet.tet[8] != NULL) {
      for (i = 0; i < 6; i++) {
        searchtet.loc = edge2locver[i][0];
        searchtet.ver = edge2locver[i][1];
        tssdissolve1(searchtet);
      }
      searchtet.tet[8] = NULL;
    }
    searchtet.tet = tetrahedrontraverse();
  }
  // Now no segment is bonded to tets.
  checksubsegs = 0;
  // Delete the memory.
  tet2segpool->restart();
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// infecthull()    Virally infect all of the tetrahedra of the convex hull   //
//                 that are not protected by subfaces.  Where there are      //
//                 subfaces, set boundary markers as appropriate.            //
//                                                                           //
// Memorypool 'viri' is used to return all the infected tetrahedra.          //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::infecthull(memorypool *viri)
{
  triface tetloop, tsymtet;
  tetrahedron **deadtet;
  face hullface;
  // point horg, hdest, hapex;

  if (b->verbose > 1) {
    //printf("  Marking concavities for elimination.\n");
  }
  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  while (tetloop.tet != (tetrahedron *) NULL) {
    // Is this tetrahedron on the hull?
    for (tetloop.loc = 0; tetloop.loc < 4; tetloop.loc++) {
      sym(tetloop, tsymtet);
      if (tsymtet.tet == dummytet) {
        // Is the tetrahedron protected by a subface?
        tspivot(tetloop, hullface);
        if (hullface.sh == dummysh) {
          // The tetrahedron is not protected; infect it.
          if (!infected(tetloop)) {
            infect(tetloop);
            deadtet = (tetrahedron **) viri->alloc();
            *deadtet = tetloop.tet;
            break;  // Go and get next tet.
          }
        } else {
          // The tetrahedron is protected; set boundary markers if appropriate.
          if (shellmark(hullface) == 0) {
            setshellmark(hullface, 1);
            /*
            horg = sorg(hullface);
            hdest = sdest(hullface);
            hapex = sapex(hullface);
            if (pointmark(horg) == 0) {
              setpointmark(horg, 1);
            }
            if (pointmark(hdest) == 0) {
              setpointmark(hdest, 1);
            }
            if (pointmark(hapex) == 0) {
              setpointmark(hapex, 1);
            }
            */
          }
        }
      }
    }
    tetloop.tet = tetrahedrontraverse();
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// plague()    Spread the virus from all infected tets to any neighbors not  //
//             protected by subfaces.                                        //
//                                                                           //
// This routine identifies all the tetrahedra that will die, and marks them  //
// as infected.  They are marked to ensure that each tetrahedron is added to //
// the virus pool only once, so the procedure will terminate. 'viri' returns //
// all infected tetrahedra which are outside the domian.                     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::plague(memorypool *viri)
{
  tetrahedron **virusloop;
  tetrahedron **deadtet;
  triface testtet, neighbor;
  face neighsh, testseg;
  face spinsh, casingin, casingout;
  int firstdadsub;
  int i;

  if (b->verbose > 1) {
    //printf("  Marking neighbors of marked tetrahedra.\n");
  }
  firstdadsub = 0;
  // Loop through all the infected tetrahedra, spreading the virus to
  //   their neighbors, then to their neighbors' neighbors.
  viri->traversalinit();
  virusloop = (tetrahedron **) viri->traverse();
  while (virusloop != (tetrahedron **) NULL) {
    testtet.tet = *virusloop;
    // Temporarily uninfect this tetrahedron, not necessary.
    uninfect(testtet);
    // Check each of the tetrahedron's four neighbors.
    for (testtet.loc = 0; testtet.loc < 4; testtet.loc++) {
      // Find the neighbor.
      sym(testtet, neighbor);
      // Check for a shell between the tetrahedron and its neighbor.
      tspivot(testtet, neighsh);
      // Check if the neighbor is nonexistent or already infected.
      if ((neighbor.tet == dummytet) || infected(neighbor)) {
        if (neighsh.sh != dummysh) {
          // There is a subface separating the tetrahedron from its neighbor,
          //   but both tetrahedra are dying, so the subface dies too.
          // Before deallocte this subface, dissolve the connections between
          //   other subfaces, subsegments and tetrahedra.
          neighsh.shver = 0;
          if (!firstdadsub) {
            firstdadsub = 1; // Report the problem once.
            if (!b->quiet) {
              //printf("Warning:  Detecting an open face (%d, %d, %d).\n",
                   //  pointmark(sorg(neighsh)), pointmark(sdest(neighsh)),
                    // pointmark(sapex(neighsh)));
            }
          }
          // For keep the same enext() direction.
          findedge(&testtet, sorg(neighsh), sdest(neighsh));
          for (i = 0; i < 3; i++) {
            sspivot(neighsh, testseg);
            if (testseg.sh != dummysh) {
              // A subsegment is found at this side, dissolve this subface
              //   from the face link of this subsegment.
              testseg.shver = 0;
              spinsh = neighsh;
              if (sorg(spinsh) != sorg(testseg)) {
                sesymself(spinsh);
              }
              spivot(spinsh, casingout);
              if ((casingout.sh == spinsh.sh) || (casingout.sh == dummysh)) {
                // This is a trivial face link, only 'neighsh' itself,
                //   the subsegment at this side is also died.
                shellfacedealloc(subsegs, testseg.sh);
              } else {
                spinsh = casingout;
                do {
                  casingin = spinsh;
                  spivotself(spinsh);
                } while (spinsh.sh != neighsh.sh);
                // Set the link casingin->casingout.
                sbond1(casingin, casingout);
                // Bond the subsegment anyway.
                ssbond(casingin, testseg);
              }
            }
            senextself(neighsh);
            enextself(testtet);
          }
          if (neighbor.tet != dummytet) {
            // Make sure the subface doesn't get deallocated again later
            //   when the infected neighbor is visited.
            tsdissolve(neighbor);
          }
          // This subface has been separated.
          if (in->mesh_dim > 2) {
            shellfacedealloc(subfaces, neighsh.sh);
          } else {
            // Dimension is 2. keep it for output.
            // Dissolve tets at both sides of this subface.
            stdissolve(neighsh);
            sesymself(neighsh);
            stdissolve(neighsh);
          }
        }
      } else {                   // The neighbor exists and is not infected.
        if (neighsh.sh == dummysh) {
          // There is no subface protecting the neighbor, infect it.
          infect(neighbor);
          // Ensure that the neighbor's neighbors will be infected.
          deadtet = (tetrahedron **) viri->alloc();
          *deadtet = neighbor.tet;
        } else {               // The neighbor is protected by a subface.
          // Remove this tetrahedron from the subface.
          stdissolve(neighsh);
          // The subface becomes a boundary.  Set markers accordingly.
          if (shellmark(neighsh) == 0) {
            setshellmark(neighsh, 1);
          }
          // This side becomes hull. Update the handle in dummytet.
          dummytet[0] = encode(neighbor);
        }
      }
    }
    // Remark the tetrahedron as infected, so it doesn't get added to the
    //   virus pool again.
    infect(testtet);
    virusloop = (tetrahedron **) viri->traverse();
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// regionplague()    Spread regional attributes and/or volume constraints    //
//                   (from a .poly file) throughout the mesh.                //
//                                                                           //
// This procedure operates in two phases.  The first phase spreads an attri- //
// bute and/or a volume constraint through a (facet-bounded) region.  The    //
// second phase uninfects all infected tetrahedra, returning them to normal. //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::
regionplague(memorypool *regionviri, REAL attribute, REAL volume)
{
  tetrahedron **virusloop;
  tetrahedron **regiontet;
  triface testtet, neighbor;
  face neighsh;

  if (b->verbose > 1) {
    //printf("  Marking neighbors of marked tetrahedra.\n");
  }
  // Loop through all the infected tetrahedra, spreading the attribute
  //   and/or volume constraint to their neighbors, then to their neighbors'
  //   neighbors.
  regionviri->traversalinit();
  virusloop = (tetrahedron **) regionviri->traverse();
  while (virusloop != (tetrahedron **) NULL) {
    testtet.tet = *virusloop;
    // Temporarily uninfect this tetrahedron, not necessary.
    uninfect(testtet);
    if (b->regionattrib) {
      // Set an attribute.
      setelemattribute(testtet.tet, in->numberoftetrahedronattributes,
                       attribute);
    }
    if (b->varvolume) {
      // Set a volume constraint.
      setvolumebound(testtet.tet, volume);
    }
    // Check each of the tetrahedron's four neighbors.
    for (testtet.loc = 0; testtet.loc < 4; testtet.loc++) {
      // Find the neighbor.
      sym(testtet, neighbor);
      // Check for a subface between the tetrahedron and its neighbor.
      tspivot(testtet, neighsh);
      // Make sure the neighbor exists, is not already infected, and
      //   isn't protected by a subface, or is protected by a nonsolid
      //   subface.
      if ((neighbor.tet != dummytet) && !infected(neighbor)
          && (neighsh.sh == dummysh)) {
        // Infect the neighbor.
        infect(neighbor);
        // Ensure that the neighbor's neighbors will be infected.
        regiontet = (tetrahedron **) regionviri->alloc();
        *regiontet = neighbor.tet;
      }
    }
    // Remark the tetrahedron as infected, so it doesn't get added to the
    //   virus pool again.
    infect(testtet);
    virusloop = (tetrahedron **) regionviri->traverse();
  }

  // Uninfect all tetrahedra.
  if (b->verbose > 1) {
    //printf("  Unmarking marked tetrahedra.\n");
  }
  regionviri->traversalinit();
  virusloop = (tetrahedron **) regionviri->traverse();
  while (virusloop != (tetrahedron **) NULL) {
    testtet.tet = *virusloop;
    uninfect(testtet);
    virusloop = (tetrahedron **) regionviri->traverse();
  }
  // Empty the virus pool.
  regionviri->restart();
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// removeholetets()    Remove tetrahedra which are outside the domain.       //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::removeholetets(memorypool* viri)
{
  tetrahedron **virusloop;
  triface testtet, neighbor;
  point checkpt;
  int *tetspernodelist;
  int i, j;

  if (b->verbose > 1) {
    //printf("  Deleting marked tetrahedra.\n");
  }

  // Create and initialize 'tetspernodelist'.
  tetspernodelist = new int[points->items + 1];
  for (i = 0; i < points->items + 1; i++) tetspernodelist[i] = 0;
  
  // Loop the tetrahedra list, counter the number of tets sharing each node.
  tetrahedrons->traversalinit();
  testtet.tet = tetrahedrontraverse();
  while (testtet.tet != (tetrahedron *) NULL) {
    // Increment the number of sharing tets for each endpoint.
    for (i = 0; i < 4; i++) {
      j = pointmark((point) testtet.tet[4 + i]);
      tetspernodelist[j]++;
    }
    testtet.tet = tetrahedrontraverse();
  }

  viri->traversalinit();
  virusloop = (tetrahedron **) viri->traverse();
  while (virusloop != (tetrahedron **) NULL) {
    testtet.tet = *virusloop;
    // Record changes in the number of boundary faces, and disconnect
    //   dead tetrahedra from their neighbors.
    for (testtet.loc = 0; testtet.loc < 4; testtet.loc++) {
      sym(testtet, neighbor);
      if (neighbor.tet == dummytet) {
        // There is no neighboring tetrahedron on this face, so this face
        //   is a boundary face.  This tetrahedron is being deleted, so this
        //   boundary face is deleted.
        hullsize--;
      } else {
        // Disconnect the tetrahedron from its neighbor.
        dissolve(neighbor);
        // There is a neighboring tetrahedron on this face, so this face
        //   becomes a boundary face when this tetrahedron is deleted.
        hullsize++;
      }
    }
    // Check the four corners of this tet if they're isolated.
    for (i = 0; i < 4; i++) {
      checkpt = (point) testtet.tet[4 + i];
      j = pointmark(checkpt);
      tetspernodelist[j]--;
      if (tetspernodelist[j] == 0) {
        // If it is added volume vertex or '-j' is not used, delete it.
        if ((pointtype(checkpt) == FREEVOLVERTEX) || !b->nojettison) { 
          setpointtype(checkpt, UNUSEDVERTEX);
          unuverts++;
        }
      }
    }
    // Return the dead tetrahedron to the pool of tetrahedra.
    tetrahedrondealloc(testtet.tet);
    virusloop = (tetrahedron **) viri->traverse();
  }
  
  delete [] tetspernodelist;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// assignregionattribs()    Assign each tetrahedron a region number.         //
//                                                                           //
// This routine is called when '-AA' switch is specified.  Every tetrahedron //
// of a (bounded) region will get a integer number to that region.  Default, //
// regions are numbered as 1, 2, 3, etc. However, if a number has already    //
// been used (set by user in the region section in .poly or .smesh), it is   //
// skipped and the next available number will be used.                       //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::assignregionattribs()
{
  list *regionnumlist;
  list *regiontetlist;
  triface tetloop, regiontet, neightet;
  face checksh;
  bool flag;
  int regionnum, num;
  int attridx, count;
  int i;

  if (b->verbose > 1) {
    //printf("  Assign region numbers.\n");
  }

  regionnumlist = new list(sizeof(int), NULL, 256);
  regiontetlist = new list(sizeof(triface), NULL, 1024);
  attridx = in->numberoftetrahedronattributes;  

  // Loop through all tets. Infect tets which already have a region number,
  //   and save the used numbers in 'regionnumlist'.
  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  while (tetloop.tet != (tetrahedron *) NULL) {
    if (!infected(tetloop)) {
      regionnum = (int) elemattribute(tetloop.tet, attridx);
      if (regionnum != 0.0) {
        // Found a numbered region tet.
        infect(tetloop);
        regiontetlist->append(&tetloop);
        // Found and infect all tets in this region.
        for (i = 0; i < regiontetlist->len(); i++) {
          regiontet = * (triface *)(* regiontetlist)[i];
          for (regiontet.loc = 0; regiontet.loc < 4; regiontet.loc++) {
            // Is there a boundary face?
            tspivot(regiontet, checksh);
            if (checksh.sh == dummysh) {
              sym(regiontet, neightet);
              if ((neightet.tet != dummytet) && !infected(neightet)) {
#ifdef SELF_CHECK
                // neightet should have the same region number. Check it.
                num = (int) elemattribute(neightet.tet, attridx);
                //assert(num == regionnum);
#endif
                infect(neightet);
                regiontetlist->append(&neightet);
              }
            }
          }
        }
        // Add regionnum to list if it is not exist.
        flag = false;
        for (i = 0; i < regionnumlist->len() && !flag; i++) {
          num = * (int *)(* regionnumlist)[i];
          flag = (num == regionnum);
        }
        if (!flag) regionnumlist->append(&regionnum);
        // Clear list for the next region.
        regiontetlist->clear();
      }
    }
    tetloop.tet = tetrahedrontraverse();
  }
  
  if (b->verbose) {
    //printf("  %d user-specified regions.\n", regionnumlist->len());
  }

  // Now loop the tets again. Assign region numbers to uninfected tets.
  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  regionnum = 1;  // Start region number.
  count = 0;
  while (tetloop.tet != (tetrahedron *) NULL) {
    if (!infected(tetloop)) {
      // An unassigned region tet.
      count++;
      do {
        flag = false;
        // Check if the region number has been used.
        for (i = 0; i < regionnumlist->len() && !flag; i++) {
          num = * (int *)(* regionnumlist)[i];
          flag = (num == regionnum);
        }
        if (flag) regionnum++;
      } while (flag);      
      setelemattribute(tetloop.tet, attridx, (REAL) regionnum);
      infect(tetloop);
      regiontetlist->append(&tetloop);
      // Found and infect all tets in this region.
      for (i = 0; i < regiontetlist->len(); i++) {
        regiontet = * (triface *)(* regiontetlist)[i];
        for (regiontet.loc = 0; regiontet.loc < 4; regiontet.loc++) {
          // Is there a boundary face?
          tspivot(regiontet, checksh);
          if (checksh.sh == dummysh) {
            sym(regiontet, neightet);
            if ((neightet.tet != dummytet) && !infected(neightet)) {
#ifdef SELF_CHECK
              // neightet should have not been assigned yet. Check it.
              num = (int) elemattribute(neightet.tet, attridx);
              //assert(num == 0);
#endif
              setelemattribute(neightet.tet, attridx, (REAL) regionnum);
              infect(neightet);
              regiontetlist->append(&neightet);
            }
          }
        }
      }
      regiontetlist->clear();
      regionnum++; // The next region number.
    }
    tetloop.tet = tetrahedrontraverse();
  }

  // Uninfect all tets.
  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  while (tetloop.tet != (tetrahedron *) NULL) {
#ifdef SELF_CHECK
    //assert(infected(tetloop));
#endif
    uninfect(tetloop);
    tetloop.tet = tetrahedrontraverse();
  }
  
  if (b->verbose) {
    //printf("  %d regions are numbered.\n", count);
  }

  delete regionnumlist;
  delete regiontetlist;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// carveholes()    Find the holes and infect them.  Find the volume          //
//                 constraints and infect them.  Infect the convex hull.     //
//                 Spread the infection and kill tetrahedra.  Spread the     //
//                 volume constraints.                                       //
//                                                                           //
// This routine mainly calls other routines to carry out all these functions.//
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::carveholes()
{
  memorypool *holeviri, *regionviri;
  tetrahedron *tptr, **holetet, **regiontet;
  triface searchtet, *holetets, *regiontets;
  enum locateresult intersect;
  int i;

  if (!b->quiet) {
    //printf("Removing exterior tetrahedra.\n");
    if ((b->verbose > 1) && (in->numberofholes > 0)) {
      //printf("  Marking holes for elimination.\n");
    }
  }

  // Initialize a pool of viri to be used for holes, concavities.
  holeviri = new memorypool(sizeof(tetrahedron *), 1024, POINTER, 0);
  // Mark as infected any unprotected tetrahedra on the boundary.
  infecthull(holeviri);

  if (in->numberofholes > 0) {
    // Allocate storage for the tetrahedra in which hole points fall.
    holetets = (triface *) new triface[in->numberofholes];
    // Infect each tetrahedron in which a hole lies.
    for (i = 0; i < 3 * in->numberofholes; i += 3) {
      // Ignore holes that aren't within the bounds of the mesh.
      if ((in->holelist[i] >= xmin) && (in->holelist[i] <= xmax)
          && (in->holelist[i + 1] >= ymin)
          && (in->holelist[i + 1] <= ymax)
          && (in->holelist[i + 2] >= zmin)
          && (in->holelist[i + 2] <= zmax)) {
        searchtet.tet = dummytet;
        // Find a tetrahedron that contains the hole.
        intersect = locate(&in->holelist[i], &searchtet);
        if ((intersect != OUTSIDE) && (!infected(searchtet))) {
          // Record the tetrahedron for processing carve hole.
          holetets[i / 3] = searchtet;
        }
      }
    }
    // Infect the hole tetrahedron.  This is done by marking the tet as
    //   infected and including the tetrahedron in the virus pool.
    for (i = 0; i < in->numberofholes; i++) {
      infect(holetets[i]);
      holetet = (tetrahedron **) holeviri->alloc();
      *holetet = holetets[i].tet;
    }
    // Free up memory.
    delete [] holetets;
  }

  // Mark as infected all tets of the holes and concavities.
  plague(holeviri);
  // The virus pool contains all outside tets now.

  // Is -A switch in use.
  if (b->regionattrib) {
    // Assign every tetrahedron a regional attribute of zero.
    tetrahedrons->traversalinit();
    tptr = tetrahedrontraverse();
    while (tptr != (tetrahedron *) NULL) {
      setelemattribute(tptr, in->numberoftetrahedronattributes, 0.0);
      tptr = tetrahedrontraverse();
    }
  }

  if (in->numberofregions > 0) {
    if (b->verbose > 1) {
      if (b->regionattrib) {
        if (b->varvolume) {
          //printf("Spreading regional attributes and volume constraints.\n");
        } else {
          //printf("Spreading regional attributes.\n");
        }
      } else {
        //printf("Spreading regional volume constraints.\n");
      }
    }
    // Allocate storage for the tetrahedra in which region points fall.
    regiontets = (triface *) new triface[in->numberofregions];
    // Find the starting tetrahedron for each region.
    for (i = 0; i < in->numberofregions; i++) {
      regiontets[i].tet = dummytet;
      // Ignore region points that aren't within the bounds of the mesh.
      if ((in->regionlist[5 * i] >= xmin)
           && (in->regionlist[5 * i] <= xmax)
           && (in->regionlist[5 * i + 1] >= ymin)
           && (in->regionlist[5 * i + 1] <= ymax)
           && (in->regionlist[5 * i + 2] >= zmin)
           && (in->regionlist[5 * i + 2] <= zmax)) {
        searchtet.tet = dummytet;
        // Find a tetrahedron that contains the region point.
        intersect = locate(&in->regionlist[5 * i], &searchtet);
        if ((intersect != OUTSIDE) && (!infected(searchtet))) {
          // Record the tetrahedron for processing after the
          //   holes have been carved.
          regiontets[i] = searchtet;
        }
      }
    }
    // Initialize a pool to be used for regional attrs, and/or regional
    //   volume constraints.
    regionviri = new memorypool(sizeof(tetrahedron *), 1024, POINTER, 0);
    // Find and set all regions.
    for (i = 0; i < in->numberofregions; i++) {
      if (regiontets[i].tet != dummytet) {
        // Make sure the tetrahedron under consideration still exists.
        //   It may have been eaten by the virus.
        if (!isdead(&(regiontets[i]))) {
          // Put one tetrahedron in the virus pool.
          infect(regiontets[i]);
          regiontet = (tetrahedron **) regionviri->alloc();
          *regiontet = regiontets[i].tet;
          // Apply one region's attribute and/or volume constraint.
          regionplague(regionviri, in->regionlist[5 * i + 3],
                       in->regionlist[5 * i + 4]);
          // The virus pool should be empty now.
        }
      }
    }
    // Free up memory.
    delete [] regiontets;
    delete regionviri;
  }

  // Now acutually remove the outside and hole tets.
  removeholetets(holeviri);
  // The mesh is nonconvex now.
  nonconvex = 1;

  // Update the point-to-tet map.
  makepoint2tetmap();

  if (b->regionattrib) {
    if (b->regionattrib > 1) {
      // -AA switch. Assign each tet a region number (> 0).
      assignregionattribs();
    }
    // Note the fact that each tetrahedron has an additional attribute.
    in->numberoftetrahedronattributes++;
  }

  // Free up memory.
  delete holeviri;
}

////                                                                       ////
////                                                                       ////
//// constrained_cxx //////////////////////////////////////////////////////////

//// steiner_cxx //////////////////////////////////////////////////////////////
////                                                                       ////
////                                                                       ////

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// initializecavity()    Initialize the cavity.                              //
//                                                                           //
// A cavity C is bounded by a list of faces, called fronts.  Each front f is //
// hold by a tet t adjacent to C, t is not in C (uninfected). If f is a hull //
// face, t does't exist, a fake tet t' is created to hold f. t' has the same //
// vertices as f but no opposite.  t' will be removed automatically after C  //
// is filled with new tets (by carvecavity()).                               //
//                                                                           //
// The faces of C are given in two lists. 'floorlist' is a set of subfaces,  //
// each subface has been oriented to face to the inside of C.  'ceillist' is //
// a set of tetrahedral faces. 'frontlist' returns the initialized fronts.   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::initializecavity(list* floorlist, list* ceillist,
  list* frontlist, list *ptlist, list* glueshlist)
{
  triface neightet, casingtet;
  triface faketet;
  face worksh;
  point *ppt;
  int i, j;

  // Infect all points of the re-triangulated cavity.
  for (i = 0; i < ptlist->len(); i++) {
    ppt = (point *)(* ptlist)[i];
    pinfect(*ppt);
  }

  // Initialize subfaces of C.
  for (i = 0; i < floorlist->len(); i++) {
    // Get a subface s.
    worksh = * (face *)(* floorlist)[i];
#ifdef SELF_CHECK
    // Current side of s should be empty.
    stpivot(worksh, neightet);
    //assert(neightet.tet == dummytet);
#endif
    // Do not insert it if some of its vertices are not in Mesh.
    ppt = (point *) &(worksh.sh[3]);
    for (j = 0; j < 3; j++) {
      if (!pinfected(ppt[j])) break;
    }
    if (j < 3) {
      // Found a subface lies outside the cavity. See an example in
      //   dump-SteinerRemoval-case2.lua.
      // Add this subface in glueshlist (to process it later).
      glueshlist->append(&worksh);
      // Do not add this face into frontlist.
      continue;
    }
    // Get the adjacent tet t.
    sesymself(worksh);
    stpivot(worksh, casingtet);
    // Does t exist?
    if (casingtet.tet == dummytet) {
      // Create a fake tet t' to hold f temporarily.
      maketetrahedron(&faketet);
      setorg(faketet, sorg(worksh));
      setdest(faketet, sdest(worksh));
      setapex(faketet, sapex(worksh));
      setoppo(faketet, (point) NULL); // Indicates it is 'fake'.
      tsbond(faketet, worksh);
      frontlist->append(&faketet);
    } else {
      frontlist->append(&casingtet);
    }
  }
  // Initialize tet faces of C.
  for (i = 0; i < ceillist->len(); i++) {
    // Get a tet face c.
    neightet = * (triface *) (* ceillist)[i];
#ifdef SELF_CHECK
    // The tet of c must be inside C (going to be deleted).
    //assert(infected(neightet));
#endif
    // Get the adjacent tet t.
    sym(neightet, casingtet);
    // Does t exist?
    if (casingtet.tet == dummytet) {
      // No. Create a fake tet t' to hold f temporarily.
      maketetrahedron(&faketet);
      // Be sure that the vertices of t' are CCW oriented.
      adjustedgering(neightet, CW); // CW edge ring.
      setorg(faketet, org(neightet));
      setdest(faketet, dest(neightet));
      setapex(faketet, apex(neightet));
      setoppo(faketet, (point) NULL); // Indicates it is 'fake'.
      // Bond t' to a subface if it exists.
      tspivot(neightet, worksh);
      if (worksh.sh != dummysh) {
        sesymself(worksh);
        tsbond(faketet, worksh);
      } 
      // Bond c <--> t'. So we're able to find t' and remove it.
      bond(faketet, neightet);
      // c may become uninfected due to the bond().
      infect(neightet);
      frontlist->append(&faketet);
    } else {
      frontlist->append(&casingtet);
    }
  }

  // Uninfect all points of the re-triangulated cavity.
  for (i = 0; i < ptlist->len(); i++) {
    ppt = (point *)(* ptlist)[i];
    puninfect(*ppt);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// delaunizecavvertices()    Form a DT of the vertices of a cavity.          //
//                                                                           //
// 'floorptlist' and 'ceilptlist' are the vertices of the cavity.            //
//                                                                           //
// The tets of the DT are created directly in the pool 'tetrahedrons', i.e., //
// no auxiliary data structure and memory are required.  The trick is at the //
// time they're created, there are no connections between them to the other  //
// tets in the pool. You can imagine they form an ioslated island.           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::delaunizecavvertices(triface* oldtet, list* floorptlist,
  list* ceilptlist, list* newtetlist, queue* flipque)
{
  point *insertarray;
  triface bakhulltet, newtet;
  long bakhullsize;
  long arraysize;
  bool success;
  int bakchksub;
  int i, j;

  // Prepare the array of points for inserting.
  arraysize = floorptlist->len();
  if (ceilptlist != (list *) NULL) {
    arraysize += ceilptlist->len();
  }
  insertarray = new point[arraysize];
  for (i = 0; i < floorptlist->len(); i++) {
    insertarray[i] = * (point *)(* floorptlist)[i];
  }
  if (ceilptlist != (list *) NULL) {
    for (j = 0; j < ceilptlist->len(); j++) {
      insertarray[i + j] = * (point *)(* ceilptlist)[j];
    }
  }

  // The incrflipdelaunay() is re-used. Backup global variables.
  decode(dummytet[0], bakhulltet);
  bakhullsize = hullsize;
  bakchksub = checksubfaces;
  checksubfaces = 0;
  b->verbose--;

  // Form the DT by incremental flip Delaunay algorithm. Do not jump for
  //   point location, do not merge points.
  success = incrflipdelaunay(oldtet, insertarray, arraysize, false, false, 
                             0.0, flipque);

  delete [] insertarray;

  if (success) {
    // Get a tet in D.
    decode(dummytet[0], newtet);
    newtetlist->append(&newtet);
    // Get all tets of D.
    retrievenewtets(newtetlist);
  }

  // Restore global variables.
  dummytet[0] = encode(bakhulltet);
  hullsize = bakhullsize;
  checksubfaces = bakchksub;
  b->verbose++;

  return success;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// retrievenewtets()    Retrieve the newly created tets.                     //
//                                                                           //
// On input, 'newtetlist' contains at least one alive new tet. From this tet,//
// other new tets can be found by a broadth-first searching.                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::retrievenewtets(list* newtetlist)
{
  triface searchtet, casingtet;
  int i;

  // There may be dead tets due to flip32(). Delete them first.
  for (i = 0; i < newtetlist->len(); i++) {
    searchtet = * (triface *)(* newtetlist)[i];
    if (isdead(&searchtet)) {
      newtetlist->del(i, 0); i--;
      continue;
    }
    infect(searchtet);
  }
  // It is possible that all tets are deleted. Check it. 2009-07-27.
  if (newtetlist->len() == 0) {
    // We must add a live tet to the list for the retrieving.
    decode(dummytet[0], searchtet);
    //assert(searchtet.tet != dummytet);
    //assert(!isdead(&searchtet));
    infect(searchtet);
    newtetlist->append(&searchtet);
  }
  // Find all new tets.
  for (i = 0; i < newtetlist->len(); i++) {
    searchtet = * (triface *)(* newtetlist)[i];
    for (searchtet.loc = 0; searchtet.loc < 4; searchtet.loc++) {
      sym(searchtet, casingtet);
      if ((casingtet.tet != dummytet) && !infected(casingtet)) {
        infect(casingtet);
        newtetlist->append(&casingtet);
      }
    }
  }
  // Uninfect new tets.
  for (i = 0; i < newtetlist->len(); i++) {
    searchtet = * (triface *)(* newtetlist)[i];
    uninfect(searchtet);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// insertauxsubface()    Fix an auxilary subface in place.                   //
//                                                                           //
// An auxilary subface s is fixed in D as it is a real subface, but s has no //
// vertices and neighbors. It has two uses: (1) it protects an identfied     //
// front f in D; (2) it serves the link to bond a tet in C and f later. The  //
// first neighbor of s (s->sh[0]) stores a pointer to f.                     //
//                                                                           //
// 'front' is a front f of C. idfront' t is a tet in D where f is identified //
// be a face of it. s will be fixed between t and its neighbor.              //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::insertauxsubface(triface* front, triface* idfront)
{
  triface neightet;
  face auxsh;

  // Create the aux subface s.
  makeshellface(subfaces, &auxsh);
  // Bond s <--> t.
  tsbond(*idfront, auxsh);
  // Does t's neighbor n exist?
  sym(*idfront, neightet);
  if (neightet.tet != dummytet) {
    // Bond s <--> n.
    sesymself(auxsh);
    tsbond(neightet, auxsh);
  }
  // Let s remember f.
  auxsh.sh[0] = (shellface) encode(*front);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// scoutfront()    Scout a face in D.                                        //
//                                                                           //
// Search a 'front' f in D. If f is found, return TRUE and the face of D is  //
// returned in 'idfront'. Otherwise, return FALSE.                           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::scoutfront(triface* front, triface* idfront)
{
  triface spintet;
  face checksh;  // For debug.
  point pa, pb, pc;
  enum finddirectionresult col;
  int hitbdry;

  // Let the front we're searching is abc.
  pa = org(*front);
  pb = dest(*front);

  point2tetorg(pa, *idfront);
  //assert(org(*idfront) == pa);
  recenttet = *idfront;

  // Search a tet having edge ab.
  col = finddirection(idfront, pb, tetrahedrons->items);
  if (col == RIGHTCOLLINEAR) {
    // b is just the destination.
  } else if (col == LEFTCOLLINEAR) {
    enext2self(*idfront);
    esymself(*idfront);
  } else if (col == TOPCOLLINEAR) {
    fnextself(*idfront);
    enext2self(*idfront);
    esymself(*idfront);
  } else if (col == BELOWHULL) {
    // This front must be a dangling subface outside the cavity.
    //   See an example in dump-SteinerRemoval-case2.lua.
    //assert(0);
  }

  if (dest(*idfront) == pb) {
    // Search a tet having face abc
    pc = apex(*front);
    spintet = *idfront;
    hitbdry = 0;
    do {
      if (apex(spintet) == pc) {
        // Found abc. Insert an auxilary subface s at idfront.
        // insertauxsubface(front, &spintet);
        *idfront = spintet;
        return true;
      }
      if (!fnextself(spintet)) {
        hitbdry ++;
        if (hitbdry < 2) {
          esym(*idfront, spintet);
          if (!fnextself(spintet)) {
            hitbdry ++;
          }
        }
      }
      if (apex(spintet) == apex(*idfront)) break;
    } while (hitbdry < 2);  
  }

  // f is missing in D.
  if (b->verbose > 1) {
    //printf("    Front (%d, %d, %d) is missing.\n", pointmark(pa),
          // pointmark(pb), pointmark(apex(*front)));
  }
  return false;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// gluefronts()    Glue two fronts together.                                 //
//                                                                           //
// This is a support routine for identifyfront().  Two fronts f and f1 are   //
// found indentical. This is caused by the non-coplanarity of vertices of a  //
// facet. Hence f and f1 are a subface and a tet. They are not fronts of the //
// cavity anymore. This routine glues f and f1 together.                     //
//                                                                           //
// A tet containing this front and not in the cavity is added into 'gluetet- //
// list' (either f or f1). It will be used to maintain the point-to-tet map. //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::gluefronts(triface* front, triface* front1, list* gluetetlist,
  list *glueshlist)
{
  face consh;

  // Glue f and f1 together. There're four cases:
  //   (1) both f and f1 are not fake;
  //   (2) f is not fake, f1 is fake;
  //   (3) f is fake and f1 is not fake;
  //   (4) both f and f1 are fake.
  // Case (4) should be not possible. 

  // Is there a concrete subface c at f.
  tspivot(*front, consh);
  if (consh.sh != dummysh) {
    sesymself(consh);
    tsbond(*front1, consh); // Bond: f1 <--> c.
    sesymself(consh);
    // Save this subface if it is not a temp subface. In case the mesh cavity
    //   fails, we need to restore the original state.
    if (!isdead(&consh)) { 
      // Save this subface into list. 
      glueshlist->append(&consh);
    }
  }
  // Does f hold by a fake tet.
  if (oppo(*front) == (point) NULL) {
    // f is fake. Case (3) or (4).
    //assert(oppo(*front1) != (point) NULL); // Eliminate (4).
    // Case (3).
    if (consh.sh != dummysh) {
      stdissolve(consh);  // Dissolve: c -x-> f.
    }
    // Dealloc f.
    tetrahedrondealloc(front->tet);
    // f1 becomes a hull. let 'dummytet' bond to it.
    dummytet[0] = encode(*front1);
  } else {
    // Case (1) or (2).
    bond(*front, *front1); // Bond f1 <--> f.
    // Add f into list. 
    gluetetlist->append(front);
  }
  // Is f a fake tet?
  if (!isdead(front)) {
    // No. Check for case (2).
    tspivot(*front1, consh);
    // Is f1 fake?
    if (oppo(*front1) == (point) NULL) {
      // Case (2) or (4)
      //assert(oppo(*front) != (point) NULL); // Eliminate (4).
      // Case (2).
      if (consh.sh != dummysh) {
        stdissolve(consh);  // Dissolve: c -x-> f1.
        sesymself(consh); // Bond: f <--> c.
        tsbond(*front, consh);
        // Save this subface if it is not a temp subface. In case the mesh 
        //   cavity fails, we need to restore the original state.
        if (!isdead(&consh)) { 
          // Save this subface into list. 
          glueshlist->append(&consh);
        }
      }
      // Dissolve: f -x->f1.
      dissolve(*front);
      // Dealloc f1.
      tetrahedrondealloc(front1->tet);
      // f becomes a hull. let 'dummytet' bond to it.
      dummytet[0] = encode(*front);
    } else {
      // Case (1).
      if (consh.sh != dummysh) {
        sesymself(consh);
        tsbond(*front, consh); // Bond: f <--> c.
        // Save this subface if it is not a temp subface. In case the mesh
        //  cavity fails, we need to restore the original state.
        if (!isdead(&consh)) { 
          // Save this subface into list. 
          glueshlist->append(&consh);
        }
      }
      // Add f1 into list. 
      gluetetlist->append(front1);
    }
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// identifyfronts()    Identify cavity faces in D.                           //
//                                                                           //
// 'frontlist' are fronts of C need indentfying.  This routine searches each //
// front f in D. Once f is found, an auxilary subface s is inserted in D at  //
// the face. If f is not found in D, remove it from frontlist and save it in //
// 'misfrontlist'.                                                           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::identifyfronts(list* frontlist, list* misfrontlist,
  list* gluetetlist,  list* glueshlist)
{
  triface front, front1, tfront;
  triface idfront, neightet;
  face auxsh, checksh;
  int len, i, j;

  misfrontlist->clear();

  // Identify all fronts in D.
  for (i = 0; i < frontlist->len(); i++) {
    // Get a front f.
    front = * (triface *)( *frontlist)[i];
    if (scoutfront(&front, &idfront)) {
      // Found f. Insert an aux subface s.
      tspivot(idfront, auxsh); 
      if (auxsh.sh != dummysh) { // Does s already exist?
        // There're two identical fronts, f (front) and f1 (s.sh[0])!
        decode((tetrahedron) auxsh.sh[0], front1);
        //assert((front1.tet != dummytet) && !infected(front1));
        // Detach s in D.
        tsdissolve(idfront);
        sym(idfront, neightet);
        if (neightet.tet != dummytet) {
          tsdissolve(neightet);
        }
        // s has fulfilled its duty. Can be deleted.
        shellfacedealloc(subfaces, auxsh.sh);
        // Remove f from frontlist.
        frontlist->del(i, 1); i--;
        // Remove f1 from frontlist.
        len = frontlist->len();
        for (j = 0; j < frontlist->len(); j++) {
          tfront = * (triface *)(* frontlist)[j];
          if ((tfront.tet == front1.tet) && (tfront.loc == front1.loc)) {
            // Found f1 in list.  Check f1 != f.
            //assert((tfront.tet != front.tet) || (tfront.loc != front.loc));
            frontlist->del(j, 1); i--;
            break;
          }
        }
        //assert((frontlist->len() + 1) == len);
        // Glue f and f1 together.
        gluefronts(&front, &front1, gluetetlist, glueshlist);        
      } else {
        // Insert an aux subface to protect f in D.
        insertauxsubface(&front, &idfront);
      }
    } else {
      // f is missing.
      frontlist->del(i, 1); i--;
      // Are there two identical fronts, f (front) and f1 (front1)?
      for (j = 0; j < misfrontlist->len(); j++) {
        front1 = * (triface *)(* misfrontlist)[j];
        if (isfacehaspoint(&front1, org(front)) &&
            isfacehaspoint(&front1, dest(front)) &&
            isfacehaspoint(&front1, apex(front))) break;
      }
      if (j < misfrontlist->len()) {
        // Found an identical front f1. Remove f1 from the list.
        misfrontlist->del(j, 1);
        // Glue f and f1 together.
        gluefronts(&front, &front1, gluetetlist, glueshlist); 
      } else {
        // Add f into misfrontlist.
        misfrontlist->append(&front);
      }
    }
  }
  return misfrontlist->len() == 0;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// detachauxsubfaces()    Detach auxilary subfaces in D.                     //
//                                                                           //
// This is a reverse routine of identifyfronts(). Some fronts are missing in //
// D. C can not be easily tetrahedralized. It needs remediation (expansion,  //
// or constrained flips, or adding a Steiner point).  This routine detaches  //
// the auxilary subfaces have been inserted in D and delete them.            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::detachauxsubfaces(list* newtetlist)
{
  triface newtet, neightet;
  face auxsh;
  int i;

  for (i = 0; i < newtetlist->len(); i++) {
    // Get a new tet t.
    newtet = * (triface *)(* newtetlist)[i];
    // t may e dead due to flips.
    if (isdead(&newtet)) continue;
    //assert(!infected(newtet));
    // Check the four faces of t.
    for (newtet.loc = 0; newtet.loc < 4; newtet.loc++) {
      tspivot(newtet, auxsh);
      if (auxsh.sh != dummysh) {
        // An auxilary subface s.
        //assert(sorg(auxsh) == (point) NULL);
        tsdissolve(newtet);  // t -x-> s.
        sym(newtet, neightet);
        if (neightet.tet != dummytet) {
          //assert(!isdead(&neightet));
          tsdissolve(neightet); // n -x-> s.
        }
        // Delete s.
        shellfacedealloc(subfaces, auxsh.sh);
      }
    }
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// carvecavity()    Remove redundant (outside) tetrahedra from D.            //
//                                                                           //
// The fronts of C have been identified in D. Hence C can be tetrahedralized //
// by removing the tets outside C. The CDT is then updated by filling C with //
// the remaining tets (inside C) of D.                                       //
//                                                                           //
// Each front is protected by an auxilary subface s in D. s has a pointer to //
// f (s.sh[0]). f can be used to classified the in- and out- tets of C (the  //
// CW orientation of f faces to the inside of C). The classified out-tets of //
// C are marked (infected) for removing.                                     //
//                                                                           //
// Notice that the out-tets may not only the tets on the CH of C,  but also  //
// tets completely inside D, eg., there is a "hole" in D.  Such tets must be //
// marked during classification. The hole tets are poped up and removed too. //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::carvecavity(list* newtetlist, list* outtetlist, 
  list* gluetetlist, queue* flipque)
{
  triface newtet, neightet, front, intet, outtet, oldtet;
  face auxsh, consh;
  point pa, pb, pc;
  point pointptr;
  REAL ori;
  bool success;
  int i;

  // Clear work list.
  outtetlist->clear();
  success = true;

  // Classify in- and out- tets in D. Mark and queue classified out-tets.
  for (i = 0; i < newtetlist->len() && success; i++) {
    // Get a new tet t.
    newtet = * (triface *)(* newtetlist)[i];
    //assert(!isdead(&newtet));
    // Skip an infected tet (it's an out tet).
    if (!infected(newtet)) {
      // Look for aux subfaces attached at t.
      for (newtet.loc = 0; newtet.loc < 4; newtet.loc++) {
        tspivot(newtet, auxsh);
        if (auxsh.sh != dummysh) {
          // Get the front f.
          decode((tetrahedron) auxsh.sh[0], front);
          // Let f face to the inside of C.
          adjustedgering(front, CW);
          pa = org(front);
          pb = dest(front);
          pc = apex(front);
          // Has this side a neighbor n? 
          sym(newtet, neightet);
          if ((neightet.tet != dummytet) && !infected(neightet)) {
            // Classify t and n (one is "in" and another is "out").
            ori = orient3d(pa, pb, pc, oppo(newtet));
            if (ori == 0.0) {
              //printf("Internal error at front %d.\n", i);
              //assert(0);
            }
            if (ori < 0.0) {
              // t is in-tet. n is out-tet.
              outtet = neightet;
              intet = newtet;
            } else {
              // n is in-tet. t is out-tet.
              outtet = newtet;
              intet = neightet;
            }
            if (!infected(outtet)) {
              // Check the special case: if this tet is protected by four 
              //  subfaces, i.e., all 4 faces of this tet are fronts. 
              //  See an example in dbg/dump-SteinerRemoval-case3.lua
              neightet = outtet;
              for (neightet.loc = 0; neightet.loc < 4; neightet.loc++) {
                tspivot(neightet, auxsh);
                if (auxsh.sh == dummysh) break;
              }
              if (neightet.loc < 4) {
                // It is an outside tet. Add it into list.
                infect(outtet);
                outtetlist->append(&outtet);
              }
            }
          } else {
            intet = newtet;
          }
          // Make sure that the intet is not iversed. 
          ori = orient3d(pa, pb, pc, oppo(intet));
          //assert(ori != 0);
          if (ori > 0) {
            // Found an inversed inside tet. Stop and return.
            if (b->verbose > 1) {
              //printf("    Intet x%lx %d (%d, %d, %d, %d) is iversed.\n", 
                //(unsigned long) intet.tet, intet.loc, pointmark(pa),
                //pointmark(pb), pointmark(pc), pointmark(oppo(intet)));
            }
            success = false;
            break;
          }
        } else {
          // This side is not protected. Check if it is a hull face.
          // Comment: This check is necessary. It is possible that all 
          //   protected subfaces have been moved in 'gluetetlist'. 
          //   If so, without this check, the newtets become orphans
          //   and remain in the output. 2009-07-29.
          sym(newtet, neightet);
          if (neightet.tet == dummytet) {
            // Found an out tet.
            if (!infected(newtet)) {
              infect(newtet);
              outtetlist->append(&newtet);
            }
            break;
          }
        }
      } // for (newtet.loc)
    } // if (!infected)
  }

  if (!success) {
    // Found inversed tet. The carvecavity failed.
    for (i = 0; i < outtetlist->len(); i++) {
      outtet = * (triface *)(* outtetlist)[i];
      uninfect(outtet);
    }
    outtetlist->clear();
    return false;
  }

  // Find and mark all out-tets.
  for (i = 0; i < outtetlist->len(); i++) {
    outtet = * (triface *)(* outtetlist)[i];
    for (outtet.loc = 0; outtet.loc < 4; outtet.loc++) {
      sym(outtet, neightet);
      // Does the neighbor exist and unmarked?
      if ((neightet.tet != dummytet) && !infected(neightet)) {
        // Is it protected by an aux subface?
        tspivot(outtet, auxsh);
        if (auxsh.sh == dummysh) {
          // It's an out-tet.
          infect(neightet);
          outtetlist->append(&neightet);
        }
      }
    }
  }

  // Remove the out- (and hole) tets.
  for (i = 0; i < outtetlist->len(); i++) {
    // Get an out-tet t.
    outtet = * (triface *)(* outtetlist)[i];
    //assert(!isdead(&outtet));
    // Detach t from the in-tets.
    for (outtet.loc = 0; outtet.loc < 4; outtet.loc++) {
      // Is there an aux subface s?
      tspivot(outtet, auxsh);
      if (auxsh.sh != dummysh) {
        // Get the neighbor n.
        sym(outtet, neightet);
        // //assert(!infected(neightet)); // t must be in-tet.
        if (infected(neightet)) {
          //printf("Error:  A front face (%d, %d, %d) x%lx got deleted.\n",
            //pointmark(org(neightet)), pointmark(dest(neightet)),
           // pointmark(apex(neightet)), (unsigned long) auxsh.sh);
          //printf("  p:draw_tet(%d, %d, %d, %d) -- in\n",
           // pointmark(org(neightet)), pointmark(dest(neightet)), 
           // pointmark(apex(neightet)), pointmark(oppo(neightet)));
          //printf("  p:draw_tet(%d, %d, %d, %d) -- out\n", 
           // pointmark(org(outtet)), pointmark(dest(outtet)), 
           // pointmark(apex(outtet)), pointmark(oppo(outtet)));
          //assert(0);
        }
        // Detach n -x-> t.
        dissolve(neightet);
      }
    }
    // Dealloc the tet.
    tetrahedrondealloc(outtet.tet);
  }

  // Connect the in-tets of C to fronts. Remove aux subfaces and fake tets.
  for (i = 0; i < newtetlist->len(); i++) {
    // Get a new tet t.
    newtet = * (triface *)(* newtetlist)[i];
    // t may be an out-tet and has got deleted.
    if (isdead(&newtet)) continue;
    // t is an in-tet. Look for aux subfaces attached at t.
    for (newtet.loc = 0; newtet.loc < 4; newtet.loc++) {
      // Is there an aux subface s?
      tspivot(newtet, auxsh);
      if (auxsh.sh != dummysh) {
        // Get the front f.
        decode((tetrahedron) auxsh.sh[0], front);
        //assert((front.tet != dummytet) && !infected(front));
        // s has fulfilled its duty. Can be deleted.
        tsdissolve(newtet); // dissolve: t -x-> s.
        // Delete s.
        shellfacedealloc(subfaces, auxsh.sh);
        // Connect the newtet t and front f.
        // Is there a concrete subface c at f.
        tspivot(front, consh);
        if (consh.sh != dummysh) {
          sesymself(consh);
          // Bond: t <--> c.
          tsbond(newtet, consh);
        }
        // Update point-to-tet map.
        pointptr = org(front);
        setpoint2tet(pointptr, encode(newtet));
        pointptr = dest(front);
        setpoint2tet(pointptr, encode(newtet));
        pointptr = apex(front);
        setpoint2tet(pointptr, encode(newtet));
        // Does f hold by a fake tet.
        if (oppo(front) == (point) NULL) {
          // f is fake.
          if (consh.sh != dummysh) {
            sesymself(consh);
            // Dissolve: c -x-> f.
            stdissolve(consh);
          }
          // Detach the fake tet from its old cavity tet. This is necessary 
          //  in case the mesh of other cavities are failed, and we have to 
          //  restore the original status.  2009-07-24.
          sym(front, oldtet);
          if (oldtet.tet != dummytet) { 
            //assert(infected(oldtet));
            dissolve(oldtet);
          }
          // Dealloc f.
          tetrahedrondealloc(front.tet);
          // f becomes a hull. let 'dummytet' bond to it.
          dummytet[0] = encode(newtet);
        } else {
          // Bond t <--> f.
          bond(newtet, front);
        }
        // t may be non-locally Delaunay and flipable.
        if (flipque != (queue *) NULL) {
          enqueueflipface(newtet, flipque);
        }
      }
    }
    // Let the corners of t2 point to it for fast searching.
    pointptr = org(newtet);
    setpoint2tet(pointptr, encode(newtet));
    pointptr = dest(newtet);
    setpoint2tet(pointptr, encode(newtet));
    pointptr = apex(newtet);
    setpoint2tet(pointptr, encode(newtet));
    pointptr = oppo(newtet);
    setpoint2tet(pointptr, encode(newtet));
  }
  // The cavity has been re-tetrahedralized.

  // Maintain point-to-tet map.
  for (i = 0; i < gluetetlist->len(); i++) {
    // Get a new tet t.
    newtet = * (triface *)(* gluetetlist)[i];
    if (isdead(&newtet)) {
      //assert(0);
    }
    pointptr = org(newtet);
    setpoint2tet(pointptr, encode(newtet));
    pointptr = dest(newtet);
    setpoint2tet(pointptr, encode(newtet));
    pointptr = apex(newtet);
    setpoint2tet(pointptr, encode(newtet));
  }

  return true;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// replacepolygonsubs()    Substitute the subfaces of a polygon.             //
//                                                                           //
// 'oldshlist' (T_old) contains the old subfaces of P.  It will be replaced  //
// by 'newshlist' (T_new) of new subfaces. Each boundary edge of P is bonded //
// to 'dummysh' in T_new.                                                    //
//                                                                           //
// Notice that Not every boundary edge of T_new is able to bond to a subface,//
// e.g., when it is a segment recovered by removing a Steiner point in it.   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::replacepolygonsubs(list* oldshlist, list* newshlist)
{
  face newsh, oldsh, spinsh;
  face casingout, casingin;
  face checkseg;
  point pa, pb;
  int i, j, k, l;

  for (i = 0; i < newshlist->len(); i++) {
    // Get a new subface s.
    newsh = * (face *)(* newshlist)[i];
    // Check the three edges of s.
    for (k = 0; k < 3; k++) {
      spivot(newsh, casingout);
      // Is it a boundary edge?
      if (casingout.sh == dummysh) {
        // Find the old subface s_o having the same edge as s.
        pa = sorg(newsh);
        pb = sdest(newsh); 
        for (j = 0; j < oldshlist->len(); j++) {
          oldsh = * (face *)(* oldshlist)[j];
	  for (l = 0; l < 3; l++) {
            if (((sorg(oldsh) == pa) && (sdest(oldsh) == pb)) ||
                ((sorg(oldsh) == pb) && (sdest(oldsh) == pa))) break;
            senextself(oldsh);
          }
          if (l < 3) break;
        }
        // Is there a matched edge?
        if (j < oldshlist->len()) {
          // Get the neighbor subface s_out.
          spivot(oldsh, casingout);
          sspivot(oldsh, checkseg);
          if (checkseg.sh != dummysh) {
            if (casingout.sh != dummysh) {
              if (oldsh.sh == casingout.sh) {
                // A subface is self-bounded. Not possible.
                //assert(0); // DEBUG
              }
              // A segment. Insert s into the face ring, ie, s_in->s->s_out.
              spinsh = casingout;
              do {
                casingin = spinsh;
                spivotself(spinsh);
              } while (sapex(spinsh) != sapex(oldsh));
              //assert(casingin.sh != oldsh.sh); 
              // Bond s_in -> s -> s_out (and dissolve s_in -> s_old -> s_out).
              sbond1(casingin, newsh);
              sbond1(newsh, casingout);
            } else {
              sbond(newsh, casingout);
            }
            // Bond the segment.
            ssbond(newsh, checkseg);
          } else {
            // Bond s <-> s_out (and dissolve s_out -> s_old).
            sbond(newsh, casingout);
          }
          // Unbound oldsh to indicate it's neighbor has been replaced.
          //   It will be used to indentfy the edge in the inverse.
          sdissolve(oldsh);
          ssdissolve(oldsh);
        }
      }
      // Go to the next edge of s.
      senextself(newsh);
    }
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// orientnewsubs()    Orient new subfaces facing to the inside of cavity.    //
//                                                                           //
// 'newshlist' contains new subfaces of the cavity C (created by re-triangu- //
// lation the polygon P). They're not necessary facing to the inside of C.   //
// 'orientsh', faces to the inside of C, is used to adjust new subfaces. The //
// normal of the new subfaces is returned in 'norm'.                         //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::orientnewsubs(list* newshlist, face* orientsh, REAL* norm)
{
  face *newsh;
  point pa, pb, pc;
  REAL ref[3], ori, len, l;
  int i;

  // Calculate the normal of 'orientsh'.
  pa = sorg(*orientsh);
  pb = sdest(*orientsh);
  pc = sapex(*orientsh);
  // facenormal(pa, pb, pc, norm, &len);
  facenormal2(pa, pb, pc, norm, 1);
  // for (i = 0; i < 3; i++) ref[i] = pa[i] + norm[i];
  len = sqrt(norm[0]*norm[0]+norm[1]*norm[1]+norm[2]*norm[2]);
  for (i = 0; i < 3; i++) norm[i] /= len;
  // Get the longest edge length of [a, b, c]
  len = distance(pa, pb);
  l = distance(pb, pc);
  if (len < l) len = l;
  l = distance(pc, pa);
  if (len < l) len = l;
  // Calculate a local above point.
  for (i = 0; i < 3; i++) ref[i] = pa[i] + len * norm[i];
  
  // Orient new subfaces. Let the normal above each one.
  for (i = 0; i < newshlist->len(); i++) {
    newsh = (face *)(* newshlist)[i];
    pa = sorg(*newsh);
    pb = sdest(*newsh);
    pc = sapex(*newsh);
    ori = orient3d(pa, pb, pc, ref);
    //assert(ori != 0.0);
    if (ori > 0.0) {
      sesymself(*newsh);
    }
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// registerelemflip()    Register an elementary flip T23, T32, or T22.       //
//                                                                           //
// If return TRUE, the flip is registered, otherwise, return FALSE, which    //
// means a conflict, this flip already exists.                               //
//                                                                           //
// Depending on the flip type, not all input points are used, those unused   //
// points are set to be dummypoint for easily processing.                    //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::registerelemflip(enum fliptype ft, point pa1, point pb1, 
  point pc1, point pa2, point pb2, point pc2)
{
  elemflip *ef;
  bool rflag;
  int i;

  rflag = false; // The flip has not registed yet.

  pinfect(pa1);
  pinfect(pb1);
  pinfect(pc1);
  pinfect(pa2);
  pinfect(pb2);
  pinfect(pc2);

  // Search the list for a registered flip.
  for (i = 0; i < (int) elemfliplist->objects; i++) {
    ef = (elemflip *) fastlookup(elemfliplist, i);
    if (ef->ft == ft) {
      rflag = (pinfected(ef->pset1[0]) && pinfected(ef->pset1[1]) &&
               pinfected(ef->pset1[2]));
      if (rflag) {
        rflag = (pinfected(ef->pset2[0]) && pinfected(ef->pset2[1]) &&
                 pinfected(ef->pset2[2]));
        if (rflag) {
          break; // This flip has been registed before.
        }
      }
    }
  }

  puninfect(pa1);
  puninfect(pb1);
  puninfect(pc1);
  puninfect(pa2);
  puninfect(pb2);
  puninfect(pc2);

  if (rflag) {
    if (b->verbose > 1) {
      //printf("    Flip: %s", ft == T23 ? "T23" : (ft == T32 ? "T32" : "T22"));
      //printf(" (%d, %d, %d) - (%d, %d, %d) is registered.\n", pointmark(pa1),
       // pointmark(pb1), pointmark(pc1), pointmark(pa2), pointmark(pb2),
       // pointmark(pc2));
    }
    return false;
  }

  // Register this flip.
  elemfliplist->newindex((void **) &ef);
  ef->ft = ft;
  ef->pset1[0] = pa1;
  ef->pset1[1] = pb1;
  ef->pset1[2] = pc1;
  ef->pset2[0] = pa2;
  ef->pset2[1] = pb2;
  ef->pset2[2] = pc2;

  return true;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// check4fixededge()    Check if the given edge [a, b] is a fixed edge.      //
//                                                                           //
// A fixed edge is saved in the "fixededgelist". Return TRUE if [a, b] has   //
// already existed in the list, otherwise, return FALSE.                     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::check4fixededge(point pa, point pb)
{
  point *ppt;
  int i;

  pinfect(pa);
  pinfect(pb);

  for (i = 0; i < (int) fixededgelist->objects; i++) {
    ppt = (point *) fastlookup(fixededgelist, i);
    if (pinfected(ppt[0]) && pinfected(ppt[1])) {
      if (b->verbose > 1) {
        //printf("    Edge (%d, %d) is fixed.\n", pointmark(pa), 
         // pointmark(pb));
      }
      break; // This edge already exists.
    }
  }

  puninfect(pa);
  puninfect(pb);

  return i < (int) fixededgelist->objects;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// removeedgebyflips()    Remove an edge by flips.                           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::removeedgebyflips(triface* remedge, int *flipcount)
{
  triface abcd; //, badc;  // Tet configuration at edge ab.
  // triface baccasing, abdcasing;
  triface abtetlist[21];  // Old configuration at ab, save maximum 20 tets.
  triface bftetlist[21];  // Old configuration at bf, save maximum 20 tets.
  triface newtetlist[90]; // New configuration after removing ab.
  face checksh;
  point pa, pb;
  bool remflag, subflag;
  int n, n1, m, i; //, j;

  triface newtet; // For update point-to-tet map.
  point *ppt;
  int j;

  pa = org(*remedge);
  pb = dest(*remedge);

  if (b->verbose > 1) {
    //printf("    Remove edge (%d, %d).\n", pointmark(pa), pointmark(pb));
  }

  // Get the tets configuration at ab. Collect maximum 10 tets.
  subflag = false;
  abcd = *remedge;
  adjustedgering(abcd, CW);
  n = 0;
  abtetlist[n] = abcd;
  do {
    // Is the list full?
    if (n == 20) break;
    // Stop if a subface appears.
    tspivot(abtetlist[n], checksh);
    if (checksh.sh != dummysh) {
      // ab is either a segment or a facet edge. The latter case is not
      //   handled yet! An edge flip is needed.
      if (b->verbose > 1) {
        //printf("    Can't remove a fixed face (%d, %d, %d).\n", pointmark(pa), 
          //pointmark(pb), pointmark(apex(abtetlist[n])));
      }
      subflag = true; break; // return false;
    }
    // Get the next tet at ab.
    if (!fnext(abtetlist[n], abtetlist[n + 1])) {
      // This edge is on the hull (2-to-2 flip case).
      subflag = true; break; // //assert(0); // Not handled yet.
    }
    n++;
  } while (apex(abtetlist[n]) != apex(abcd));

  if (subflag) {
    // The face link contains subfaces, stop.
    return false;
  }

  // Do not flip this edge if it has been fixed (for recovering a face).
  if (check4fixededge(pa, pb)) {
    return false;
  }

  // 2 < n < 20.
  if (n == 3) {
    // There are three tets at ab. Try to do a flip32 at ab.
    remflag = removeedgebyflip32(NULL, abtetlist, newtetlist, NULL);
  } else if ((n > 3) && (n <= b->maxflipedgelinksize)) {
    // Four tets case. Try to do edge transformation.
    remflag = removeedgebytranNM(NULL,n,abtetlist,newtetlist,NULL,NULL,NULL);
  } else {
    if (b->verbose > 1) {
      //printf("    !! Unhandled case: n = %d.\n", n);
    }
    remflag = false;
  }

  if (remflag) {
    // Delete the old tets.
    for (i = 0; i < n; i++) {
      tetrahedrondealloc(abtetlist[i].tet);
    }
    m = (n - 2) * 2; // The numebr of new tets.
    if (b->verbose > 1) {
      //printf("    Done flip %d-to-%d.\n", n, m);
    }
    // Update the point-to-tet map
    for (i = 0; i < m; i++) {
      newtet = newtetlist[i];
      ppt = (point *) &(newtet.tet[4]);
      for (j = 0; j < 4; j++) {
        setpoint2tet(ppt[j], encode(newtet));
      }
    }
    *flipcount = *flipcount + 1;
    return true;
  }

  if (n <= b->maxflipedgelinksize) {
    // Try to do a combination of flips.
    n1 = 0;
    remflag = removeedgebycombNM(NULL, n, abtetlist, &n1, bftetlist,
      newtetlist, NULL);
    if (remflag) {
      // Delete the old tets.
      for (i = 0; i < n; i++) {
        tetrahedrondealloc(abtetlist[i].tet);
      }
      for (i = 0; i < n1; i++) {
        if (!isdead(&(bftetlist[i]))) {
          tetrahedrondealloc(bftetlist[i].tet);
        }
      }
      m = ((n1 - 2) * 2 - 1) + (n - 3) * 2; // The number of new tets.
      if (b->verbose > 1) {
        //printf("    Done flip %d-to-%d (n-1=%d, n1=%d). ", n+n1-2, m, n-1,n1);
        //printf("\n");
      }
      // Update the point-to-tet map
      for (i = 0; i < m; i++) {
        newtet = newtetlist[i];
        ppt = (point *) &(newtet.tet[4]);
        for (j = 0; j < 4; j++) {
          setpoint2tet(ppt[j], encode(newtet));
        }
      }
      *flipcount = *flipcount + 1;
      return true;
    }
  }

  return false;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// removefacebyflips()    Remove a face by a sequence of flips.              //
//                                                                           //
// The face should not be a subface.                                         //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::removefacebyflips(triface* remface, int *flipcount)
{
  triface neightet, checkface1, checkface2;
  face checksh1, checksh2;
  point pa, pb, pc, pd, pe;
  enum interresult dir;
  REAL ori;
  int types[2], poss[4];
  int i;

  adjustedgering(*remface, CCW);
  sym(*remface, neightet);
  if (neightet.tet == dummytet) {
    // A boundary face. It is not flipable.
    return false;
  }
  pd = oppo(*remface);
  pe = oppo(neightet);

  if (b->verbose > 1) {
    //printf("    Remove face (%d, %d, %d) %d, %d\n", pointmark(org(*remface)), 
      //pointmark(dest(*remface)), pointmark(apex(*remface)), 
     // pointmark(pd), pointmark(pe));
  }

  // Do not remove this face if it is a fixed face.
  tspivot(*remface, checksh1);
  if (checksh1.sh != dummysh) {
    if (b->verbose > 1) {
      //printf("    Can't remove a fixed face (%d, %d, %d)\n", 
       // pointmark(org(*remface)), pointmark(dest(*remface)), 
       // pointmark(apex(*remface)));
    }
    return false;
  } 

  // Check if edge [d, e] intersects the flip face [a, b, c].
  for (i = 0; i < 3; i++) {
    pa = org(*remface);
    pb = dest(*remface);
    pc = apex(*remface);
    ori = orient3d(pa, pb, pd, pe);
    if (ori <= 0) break; // Coplanar or Above.
    enextself(*remface);
  }

  if (i == 3) {
    // A 2-to-3 flip is found.
    // Regist the flipping face.
    if (!registerelemflip(T23, pa, pb, pc, pd, pe, dummypoint)) {
      // Detected a potential flip loop.
      return false;
    }
    // Do a 2-to-3 flip.
    flip23(remface, NULL);
    *flipcount = *flipcount + 1;
    return true;
  }

  if (ori == 0) {
    // Check if [a, b] is a hull edge. If so a flip22() could apply.
    fnext(*remface, checkface1);
    tspivot(checkface1, checksh1);
    symedge(*remface, neightet);
    fnext(neightet, checkface2);
    tspivot(checkface2, checksh2);
    // First check if it is a protected face.
    if ((checksh1.sh == dummysh) && (checksh2.sh == dummysh)) {
      // Check if it is a hull face.
      symself(checkface1);
      symself(checkface2);
      if ((checkface1.tet == dummytet) && (checkface2.tet == dummytet)) {
        // Check if the edge [a, b] intersects [d, e] in its interior.
        if (tri_edge_test(pa, pb, pc, pd, pe, NULL, 1, types, poss)) {
          dir = (enum interresult) types[0];
          if (dir == INTEREDGE) {
            // Edge [d, e] intersects [a, b, c]. A flip 2-to-2 is found.
            // Check if the edge [a, b] is a fixed one (for recovering a face).
            if (check4fixededge(pa, pb)) {
              // [a, b] is a fixed edge. Stop the flip.
              return false; 
            }
            // Regist this flip.
            if (!registerelemflip(T22, pa, pb, dummypoint, pd, pe, 
                                  dummypoint)) {
              // Detected a potential flip loop.
              return false;
            }
            // We can flip this edge [a, b].
            flip22(remface, NULL);
            *flipcount = *flipcount + 1;
            return true;
          } else {
            // Either a or b is collinear with edge [d, e]. NOT flipable.
            //assert(dir == INTERVERT);
            return false;
          }
        } else {
          // [a,b] does not intersect with [d, e]. Don't do a 2-to-2 flip. 
          //   See an example in dbg/dump-nflip22-case.lua
          return false;
        }
      }
    } else {
      if (b->verbose > 1) {
        //printf("    Can't remove a fixed face (%d, %d, %d).\n", pointmark(pa),
          //pointmark(pb), pointmark(apex(neightet)));
      }
      return false;
    }
  }

  // The edge [d, e] does not intersect [a, b, c]. Try to flip edge [a, b].
  // Comment: We've found that the edge [a, b] is locally non-convex. It
  //   must be an interior edge.
  return removeedgebyflips(remface, flipcount);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// recoveredgebyflips()    Recover edge [a, b] by a sequence of flips.       //
//                                                                           //
// The edge to be recovered is from a = org(*searchtet) to b. Return TRUE if //
// the edge [a, b] is recovered, and retruned in 'searchtet', otherwise,     //
// return FALSE.                                                             //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::recoveredgebyflips(triface *searchtet,point pb,int *flipcount)
{
  triface remface;
  point pa;
  enum interresult dir;
  bool success;

  pa = org(*searchtet);

  if (b->verbose > 1) {
    //printf("  Recover edge (%d, %d)\n", pointmark(pa), pointmark(pb));
  }

  //assert(elemfliplist->objects == 0l);

  while (1) {

    // Go to the intersected face, try to flip it.
    enextfnext(*searchtet, remface);

    // Try to remove this crossing face.
    success = removefacebyflips(&remface, flipcount);
    if (!success) break;

    point2tetorg(pa, *searchtet);
    //assert(org(*searchtet) == pa);
    dir = finddirection2(searchtet, pb);
    if (dir == INTERVERT) {
      break; // The edge has found.
    }

  } // while (1)

  // Clear the recorded flip list.
  elemfliplist->restart();

  return success;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// recoverfacebyflips()    Recover a missing face by a sequence of flips.    //
//                                                                           //
// Assume that at least one of the edges of the face exists in the current   //
// mesh. The face is recovered by continusly removing all crossing edges of  //
// this face.                                                                //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::recoverfacebyflips(triface* front, int *flipcount)
{
  triface searchtet, spintet, bdrytet;
  triface remedge, remface;
  point pa, pb, pc, pd, pe, *ppt;
  enum interresult dir;
  bool success;
  int hitbdry;
  int i;

  if (b->verbose > 1) {
    //printf("  Recover face (%d, %d, %d)\n", pointmark(org(*front)),
      //pointmark(dest(*front)), pointmark(apex(*front)));
  }

  //assert(fixededgelist->objects == 0l);

  // First recover three edges of this face.
  for (i = 0; i < 3; i++) {
    pa = org(*front);
    pb = dest(*front);
    pc = apex(*front); // The apex.
    point2tetorg(pa, searchtet);
    //assert(org(searchtet) == pa);
    dir = finddirection2(&searchtet, pb);
    if (dir == BELOWHULL2) {
      // Detect an outside front. This happens when a new subface created by
      //   re-triangulating is actually outside the cavity. The gluefront()
      //   is not called in this case. There must be an existing face which
      //   lies outside the cavity that matches this subface.
      //   An example is dump-SteinerRemoval-case2.lua. 2009-07-06.
      // fixededgelist->restart();
      // return false; 
      //assert(0);
    }
    if (dir != INTERVERT) {
      if (!recoveredgebyflips(&searchtet, pb, flipcount)) {
        // Failed to recover edge [a, b], so does the face.
        fixededgelist->restart();
        return false; 
      }
    }
    // DIR == INTERVERT
    if (dest(searchtet) != pb) {
      // There eixsts a collonear edge but not the desired one.
      // Failed to recover edge [a, b], so does the face.
      fixededgelist->restart();
      return false;
    }
    // Save this edge to avoid flipping it away.
    fixededgelist->newindex((void **) &ppt);
    ppt[0] = pa;
    ppt[1] = pb;
    // Go to the next edge.
    enextself(*front);
  }

  //assert(elemfliplist->objects == 0l);

  while (1) {

    success = false;

    // Adjust edge [a->b] to be in the CCW edge ring.
    adjustedgering(searchtet, CCW);
    if (org(searchtet) != pa) {
      fnextself(searchtet);
      esymself(searchtet);
    }
    //assert(org(searchtet) == pa);
    //assert(dest(searchtet) == pb);

    spintet = searchtet;
    hitbdry = 0;
    do {
      if (apex(spintet) == pc) {
        // Found abc. Insert an auxilary subface s at idfront.
        insertauxsubface(front, &spintet);
        success = true; 
        break; // return true;
      }
      if (!fnextself(spintet)) {
        bdrytet = spintet; // Save the boundary face.
        hitbdry ++;
        if (hitbdry < 2) {
          esym(searchtet, spintet);
          if (!fnextself(spintet)) {
            hitbdry++;
          }
        }
      }
      if (apex(spintet) == apex(searchtet)) break;
    } while (hitbdry < 2);

    if (success) break;

    if (hitbdry > 0) {
      // Adjust searchtet to be the boundary face at edge [pa->pb].
      searchtet = bdrytet;
      adjustedgering(searchtet, CCW);
      pa = org(searchtet);
      pb = dest(searchtet);
    }

    // Comments:
    // Remember that 'front' is the face [a, b, c]. 'spintet' is a tet
    //   [a, b, d, e] at edge [a->b]. 
    // We first check if the edge [d, e] intersects face [a, b, c], if it
    //   does, we try to flip the edge [d, e] away.
    // We then check if the edge [b, c] intersects face [a, d, e], if it
    //   does, we try to flip the face [a, d, e] away.
    // We then check if the edge [a, c] intersects face [b, d, e], if it
    //   does, we try to flip the face [b, d, e] away. 

    // Search a crossing edge/face and try to flip it away.
    remedge.tet = remface.tet = NULL;
    spintet = searchtet;
    hitbdry = 0;
    do {
      pd = apex(spintet);
      pe = oppo(spintet);
      // Check if edge [d, e] intersects [a, b, c].
      if (tri_edge_test(pa, pb, pc, pd, pe, NULL, 0, NULL, NULL)) {
          remedge = spintet;
          enextfnextself(remedge);
          enextself(remedge); // Edge [pd->pe].
          break;
      }
      // Check if [b, c] intersects [a, d, e].
      if (!iscollinear(pa, pd, pe, b->epsilon)) {
        if (tri_edge_test(pa, pd, pe, pb, pc, NULL, 0, NULL, NULL)) {
          remface = spintet;
          enext2fnextself(remface); // Face [a, d, e].
          break;
        }
      }
      if (!iscollinear(pb, pd, pe, b->epsilon)) {
        // Check if [a, c] intersects [b, d, e].
        if (tri_edge_test(pb, pd, pe, pa, pc, NULL, 0, NULL, NULL)) {
          remface = spintet;
          enextfnextself(remface); // Face [b, d, e].
          break;
        }
      }
      tfnextself(spintet);
      if (spintet.tet == dummytet) {
        break; // Meet boundary.
      }
      if (apex(spintet) == apex(searchtet)) break;
    } while (hitbdry < 2);

    if (remedge.tet != NULL) {
      // Try to remove this crossing edge.
      success = removeedgebyflips(&remedge, flipcount);
    } else if (remface.tet != NULL) {
      /*// Do not flip it if it is a subface.
      tspivot(remface, checksh);
      if (checksh.sh != dummysh) {
        return false;
      }*/
      success = removefacebyflips(&remface, flipcount);
    } else {
      // Haven't found a crossing edge or face.
      success = false;
    }

    if (!success) break;

    point2tetorg(pa, searchtet);
    //assert(org(searchtet) == pa);
    dir = finddirection2(&searchtet, pb);
    //assert(dest(searchtet) == pb);

  } // while (1)

  // Clear the recored flips.
  elemfliplist->restart();
  // Clear the fixed edges.
  fixededgelist->restart(); 

  return success;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// constrainedcavity()    Tetrahedralize a cavity by constrained tetrahedra. //
//                                                                           //
// The cavity C is bounded by faces F in 'floorlist' and 'ceillist'. 'ptlist'//
// V is the set of vertices of C.                                            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::constrainedcavity(triface* oldtet, list* floorlist,
  list* ceillist, list* ptlist, list* frontlist, list* misfrontlist,
  list* newtetlist,  list* gluetetlist, list* glueshlist, queue* flipque)
{
  triface misfront, newtet;
  point pointptr;  // Used with gluetetlist.
  bool success;
  int misfacecount;
  int flipcount, totalflipcount;
  int i;

  if (b->verbose > 1) {
    //printf("    Constrained cavity (%d floors, %d ceilings, %d vertices).\n",
         //  floorlist->len(), ceillist->len(), ptlist->len());
  }

  // Initialize the cavity C.
  initializecavity(floorlist, ceillist, frontlist, ptlist, glueshlist);
  // Form the D of the vertices of C.
  success = delaunizecavvertices(oldtet, ptlist, NULL, newtetlist, flipque);

  if (success) {
    // Identify faces of C in D.
    if (!identifyfronts(frontlist, misfrontlist, gluetetlist, glueshlist)) {
      // Some faces are missing.
      totalflipcount = 0;
      // Try to recover missing faces by flips.
      do {
        misfacecount = misfrontlist->len();
        flipcount = 0;
        for (i = 0; i < misfrontlist->len(); i++) {
          // Get a missing front f.
          misfront = * (triface *)(* misfrontlist)[i];
          // Let f face toward the inside of C.
          // adjustedgering(misfront, CW);
          // if (recoverfront(&misfront, newtetlist, flipque)) {
          if (recoverfacebyflips(&misfront, &flipcount)) {
            // f has been recovered.
            frontlist->append(&misfront);
            misfrontlist->del(i, 0); i--;
          }
        }
        totalflipcount += flipcount;
        // Have all faces been recovered?
        if (misfrontlist->len() == 0) break;
        // Continue the loop if some missing faces have been recovered.
      } while (misfacecount > misfrontlist->len());
      // Retrieve new tets and purge dead tets in D.
      retrievenewtets(newtetlist);
    }
    success = (misfrontlist->len() == 0);
  } // if (success)

  if (success) {
    // All fronts have identified in D. Get the shape of C by removing out
    //   tets of C. 'misfrontlist' is reused for removing out tets.
    //   Don't do flip since the new tets may get deleted later.
    if (carvecavity(newtetlist, misfrontlist, gluetetlist, NULL)) {
      return true;
    }
  } 
  
  // {
    // Fail to tetrahedralize C.
    // Remove aux subfaces.
    detachauxsubfaces(newtetlist);
    // Remove new tets.
    for (i = 0; i < newtetlist->len(); i++) {
      newtet = * (triface *)(* newtetlist)[i];
      //assert(!isdead(&newtet));
      tetrahedrondealloc(newtet.tet);
    }
    newtetlist->clear();
    // Restore faces of C in frontlist.
    for (i = 0; i < misfrontlist->len(); i++) {
      misfront = * (triface *)(* misfrontlist)[i];
      frontlist->append(&misfront);
    }
    // Some fronts have been removed from the front list (in gluefronts()).
    // Maintain point-to-tet map.
    for (i = 0; i < gluetetlist->len(); i++) {
      // Get a tet t which lies outside the current cavity.
      newtet = * (triface *)(* gluetetlist)[i];
      if (isdead(&newtet)) {
        //assert(0);
      }
      pointptr = org(newtet);
      setpoint2tet(pointptr, encode(newtet));
      pointptr = dest(newtet);
      setpoint2tet(pointptr, encode(newtet));
      pointptr = apex(newtet);
      setpoint2tet(pointptr, encode(newtet));
    }
    return false;
  // }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// findrelocatepoint()    Find new location for relocating a point.          //
//                                                                           //
// 'frontlist' contains the boundary faces of the cavity C.  Some fronts are //
// visible by 'stpt' p, some are coplanar with p.                            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::findrelocatepoint2(point steinpt, point relocatept,
  REAL* normal, list* frontlist, list* oldtetlist)
{
  triface oldtet, front;
  point pa, pb, pc;
  REAL farpt[3], fcent[3], candpt[3], searchpt[3];
  REAL dist, factor;
  REAL ori, stepx, minvol, minvol1;
  bool stopflag;
  // int iter;
  int i, j;

  if (b->verbose > 1) {
    //printf("    Find new location for point %d.\n", pointmark(steinpt));
  }

  // Calculate a far enough point on the normal direction.
  for (i = 0; i < 3; i++) {
    farpt[i] = steinpt[i] + longest * normal[i];
  }

  // Find the face in oldtet intersecting with the line steinpt->farpt.
  for (i = 0; i < oldtetlist->len(); i++) {
    oldtet = * (triface *)(* oldtetlist)[i];
    pa = org(oldtet);
    pb = dest(oldtet);
    pc = apex(oldtet);
    if (tri_edge_test(pa, pb, pc, steinpt, farpt, farpt, 0, NULL, NULL)) {
      // projpt2face(steinpt, pa, pb, pc, fcent);
      // Find the intersected face. Calculate the intersection.
      planelineint(pa, pb, pc, steinpt, farpt, fcent, &factor);
      if (factor != 0) { // //assert(factor != 0);
        if (b->verbose > 1) {
          //printf("p:show_vector(%g, %g, %g, %g, %g, %g) -- L\n", steinpt[0], 
            //steinpt[1], steinpt[2], fcent[0], fcent[1], fcent[2]);
        }
        break;
      }
    }
  }
  // There must be an interseced face.
  if (i == oldtetlist->len()) {
    return false;  // //assert(0);
  }

  // Start search a relocating point which maximize the min. vol.
  dist = distance(steinpt, fcent);
  stepx = dist / 100.0; // Divide the segment into 100 pieces.
  minvol = 0;
  stopflag = false;
  for (i = 1; i < 100 && !stopflag; i++) {
    // Calculate a candidate point.
    for (j = 0; j < 3; j++) {
      candpt[j] = steinpt[j] + (stepx * (double) i) * (fcent[j] - steinpt[j]);
    }
    minvol1 = 0;
    for (j = 0; j < frontlist->len(); j++) {
      front = * (triface *)(* frontlist)[j];
      // Let f face inside C. (f is a face of tet adjacent to C).
      adjustedgering(front, CW);
      pa = org(front);
      pb = dest(front);
      pc = apex(front);
      ori = orient3d(pa, pb, pc, candpt);
      if (ori >= 0) {
        // An invisible front. (1) fails.
        stopflag = true;
        break; 
      }
      if (j == 0) {
        minvol1 = -ori;
      } else {
        if (minvol1 > (-ori)) {
          minvol1 = -ori;
        }
      }
    } // j
    if (!stopflag) {
      if (minvol < minvol1) {
        // The min. vol. is improved by this candidate. Choose it.
        for (j = 0; j < 3; j++) searchpt[j] = candpt[j];
        minvol = minvol1;
      } else {
        // The min. vol. begins to decrease. Stop the search.
        stopflag = true;
      }
    }
  } // i

  if (minvol > 0) {
    // We've found a valid relocation. Choose it.
    if (b->verbose > 1) {
      //printf("p:show_vector(%g, %g, %g, %g, %g, %g) -- Relo\n", steinpt[0], 
        //steinpt[1], steinpt[2], searchpt[0], searchpt[1], searchpt[2]);
    }
    for (i = 0; i < 3; i++) relocatept[i] = searchpt[i];
    return true;
  } else {
    return false;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// relocatepoint()    Relocate a point into the cavity.                      //
//                                                                           //
// 'frontlist' contains the boundary faces of the cavity C. All fronts must  //
// be visible by 'steinpt'.  Some fronts may hold by 'fake' tets (they are   //
// hull faces).  Fake tets will be removed when they're finished.            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::relocatepoint(point steinpt, triface* oldtet, list* frontlist,
  list* newtetlist, queue* flipque)
{
  triface front, newtet, newface, neightet;
  face checksh;
  point pa, pb;
  REAL attrib, volume;
  bool bdflag;
  int i, j, k, l;

  if (b->verbose > 1) {
    //printf("    Insert Steiner point (%.12g, %.12g, %.12g) %d.\n",
           //steinpt[0], steinpt[1], steinpt[2], pointmark(steinpt));
  }
  // Clear the list first.
  newtetlist->clear();

  // Create the tets formed by fronts and 'steinpt'.
  for (i = 0; i < frontlist->len(); i++) {
    // Get a front f.
    front = * (triface *)(* frontlist)[i];
    // Let f face inside C. (f is a face of tet adjacent to C).
    adjustedgering(front, CW);
    if (b->verbose > 2) {
      //printf("    Get front (%d, %d, %d).\n", pointmark(org(front)),
             //pointmark(dest(front)), pointmark(apex(front)));
    }
    maketetrahedron(&newtet);
    newtetlist->append(&newtet);
    setorg(newtet, org(front));
    setdest(newtet, dest(front));
    setapex(newtet, apex(front));
    setoppo(newtet, steinpt);
    if (oldtet != (triface *) NULL) {
      for (j = 0; j < in->numberoftetrahedronattributes; j++) {
        attrib = elemattribute(oldtet->tet, j);
        setelemattribute(newtet.tet, j, attrib);
      }
      if (b->varvolume) {
        volume = volumebound(oldtet->tet);
        setvolumebound(newtet.tet, volume);
      }
    }
    // Update the point-to-tet map.
    pa = org(front);
    setpoint2tet(pa, encode(newtet));
    pa = dest(front);
    setpoint2tet(pa, encode(newtet));
    pa = apex(front);
    setpoint2tet(pa, encode(newtet));
    setpoint2tet(steinpt, encode(newtet));
    // 'front' may be a 'fake' tet.
    tspivot(front, checksh);
    if (oppo(front) == (point) NULL) {
      if (checksh.sh != dummysh) {
        stdissolve(checksh);
      }
      // Detach the fake tet from its old cavity tet. This is necessary 
      //  in case the mesh of other cavities are failed, and we have to 
      //  restore the original status.  2009-07-28.
      sym(front, neightet);
      if (neightet.tet != dummytet) { 
        //assert(infected(neightet));
        dissolve(neightet);
      }
      // Dealloc the 'fake' tet.
      tetrahedrondealloc(front.tet);
      // This side (newtet) is a boundary face, let 'dummytet' bond to it.
      //   Otherwise, 'dummytet' may point to a dead tetrahedron after the
      //   old cavity tets are removed.
      dummytet[0] = encode(newtet);
    } else {
      // Bond two tetrahedra, also dissolve the old bond at 'front'.
      bond(newtet, front);
    }
    if (checksh.sh != dummysh) {
      sesymself(checksh);
      tsbond(newtet, checksh);
    }
    if (flipque != (queue *) NULL) {
      // f may be non-locally Delaunay and flipable.
      enqueueflipface(newtet, flipque);
    }
    // The three neighbors are open. Will be finished later.
  }

  // Connect new tets in C. All connecting faces must contain 'steinpt'.
  // The following code should be re-written. 2009-07-30.
  for (i = 0; i < newtetlist->len(); i++) {
    newtet = * (triface *)(* newtetlist)[i];
    newtet.ver = 0;
    for (j = 0; j < 3; j++) {
      fnext(newtet, newface);
      sym(newface, neightet);
      if (neightet.tet == dummytet) {
        // Find a neightet to connect it.
        bdflag = false;
        pa = org(newface);
        pb = dest(newface);
        //assert(apex(newface) == steinpt);
        for (k = i + 1; k < newtetlist->len() && !bdflag; k++) {
          neightet = * (triface *)(* newtetlist)[k];
          neightet.ver = 0;
          for (l = 0; l < 3; l++) {
            if ((org(neightet) == pa && dest(neightet) == pb) ||
                (org(neightet) == pb && dest(neightet) == pa)) {
              // Find the neighbor.
              fnextself(neightet);
              //assert(apex(neightet) == steinpt);
              // Now neightet is a face same as newface, bond them.
              bond(newface, neightet);
              bdflag = true;
              break;
            }
            enextself(neightet);
          }
        }
        if (!bdflag) {
          //assert(0); // break;  // The relocation failed.
        }
      }
      enextself(newtet);
    }
    if (j < 3) break;
  }

  if (i < newtetlist->len()) {
    // Relocation failed. Delete new tets.
    for (i = 0; i < newtetlist->len(); i++) {
      newtet = * (triface *)(* newtetlist)[i];
      tetrahedrondealloc(newtet.tet);
    }
    return false;
  }

  if (flipque != (queue *) NULL) { 
    // Recover locally Delaunay faces.
    lawson3d(flipque);
  }

  return true;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// findcollapseedge()    Find collapseable edge to suppress an endpoint.     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::findcollapseedge(point suppt, point *conpt, list* oldtetlist,
  list* ptlist)
{
  triface front;
  point pt, pa, pb, pc;
  REAL *lenarray, ltmp, ori;
  bool visflag;
  int *idxarray, itmp;
  int n, i, j;

  if (b->verbose > 2) {
    //printf("    Search an edge (in %d edges) for collapse %d.\n",
           //ptlist->len(), pointmark(suppt));
  }

  // Candidate edges are p to the points of B(p) (in 'ptlist').
  n = ptlist->len();
  lenarray = new REAL[n];
  idxarray = new int[n];
  // Sort the points of B(p) by distance to p.
  for (i = 0; i < n; i++) {
    pt = * (point *)(* ptlist)[i];
    lenarray[i] = distance(suppt, pt);
    idxarray[i] = i;
  }
  // Bubble sort.
  for (i = 0; i < n - 1; i++) {
    for (j = 0; j < n - 1 - i; j++) {
      if (lenarray[j + 1] < lenarray[j]) {  // compare the two neighbors
        ltmp = lenarray[j];           // swap a[j] and a[j + 1]
        lenarray[j] = lenarray[j + 1];
        lenarray[j + 1] = ltmp;
        itmp = idxarray[j];           // swap a[j] and a[j + 1]
        idxarray[j] = idxarray[j + 1];
        idxarray[j + 1] = itmp;
      }
    }
  }
  // For each point q of B(p), test if the edge (p, q) can be collapseed.
  for (i = 0; i < n; i++) {
    pt = * (point *)(* ptlist)[idxarray[i]];
    // Is q visible by faces of B(p) not with q as a vertex.
    lenarray[i] = 0.0; // zero volume.
    visflag = true;
    for (j = 0; j < oldtetlist->len() && visflag; j++) {
      front = * (triface *)(* oldtetlist)[j];
      // Let f face to inside of B(p).
      adjustedgering(front, CCW);
      pa = org(front);
      pb = dest(front);
      pc = apex(front);
      // Is f contains q?
      if ((pa != pt) && (pb != pt) && (pc != pt)) {
        ori = orient3d(pa, pb, pc, pt);
        if (ori != 0.0) {
          if (iscoplanar(pa, pb, pc, pt, ori, b->epsilon * 1e+2)) ori = 0.0;
        }
        visflag = ori < 0.0;
        if (visflag) {
          // Visible, set the smallest volume.
          if (j == 0) {
            lenarray[i] = fabs(ori);
          } else {
            lenarray[i] = fabs(ori) < lenarray[i] ? fabs(ori) : lenarray[i];
          }
        } else {
          // Invisible. Do not collapse (p, q).
          lenarray[i] = 0.0;
        }
      }
    }
    if ((b->verbose > 2) && visflag) {
      //printf("    Got candidate %d vol(%g).\n", pointmark(pt), lenarray[i]);
    }
  }

  // Select the largest non-zero volume (result in ltmp).
  ltmp = lenarray[0];
  itmp = idxarray[0];
  for (i = 1; i < n; i++) {
    if (lenarray[i] != 0.0) {
      if (lenarray[i] > ltmp) {
        ltmp = lenarray[i];
        itmp = idxarray[i]; // The index to find the point.
      }
    }
  }

  delete [] lenarray;
  delete [] idxarray;

  if (ltmp == 0.0) {
    // No edge can be collapseed.
    *conpt = (point) NULL;
    return false;
  } else {
    pt = * (point *)(* ptlist)[itmp];
    *conpt = pt;
    return true;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// collapseedge()    Remove a point by edge collapse.                        //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::collapseedge(point suppt, point conpt, list* oldtetlist,
  list* deadtetlist)
{
  triface oldtet, deadtet;
  triface adjtet1, adjtet2;
  face adjsh;
  point pa, pb, pc;
  int i, j;

  if (b->verbose > 2) {
    //printf("    Collapse edge (%d,%d).\n", pointmark(suppt), pointmark(conpt));
  }

  // Loop in B(p), replace p with np, queue dead tets, uninfect old tets.
  for (i = 0; i < oldtetlist->len(); i++) {
    oldtet = * (triface *)(* oldtetlist)[i]; // //assert(infected(oldtet));
    uninfect(oldtet);
    pa = org(oldtet);
    pb = dest(oldtet);
    pc = apex(oldtet);
    //assert(oppo(oldtet) == suppt);
    setoppo(oldtet, conpt);
    if ((pa == conpt) || (pb == conpt) || (pc == conpt)) {
      deadtetlist->append(&oldtet); // a collpased tet.
    } else {
      // A non-collapse tet. Update point-to-tet map.
      setpoint2tet(pa, encode(oldtet));
      setpoint2tet(pb, encode(oldtet));
      setpoint2tet(pc, encode(oldtet));
      setpoint2tet(conpt, encode(oldtet));
    }
  }
  // Loop in deadtetlist, glue adjacent tets of dead tets.
  for (i = 0; i < deadtetlist->len(); i++) {
    deadtet = * (triface *)(* deadtetlist)[i];
    // Get the adjacent tet n1 (outside B(p)).
    sym(deadtet, adjtet1);
    tspivot(deadtet, adjsh);
    // Find the edge in deadtet opposite to conpt.
    adjustedgering(deadtet, CCW);
    for (j = 0; j < 3; j++) {
      if (apex(deadtet) == conpt) break;
      enextself(deadtet);
    }
    //assert(j < 3);
    // Get another adjacent tet n2.
    fnext(deadtet, adjtet2);
    symself(adjtet2);
    //assert(adjtet2.tet != dummytet); // n2 is inside B(p).
    if (adjtet1.tet != dummytet) {
      bond(adjtet1, adjtet2); // Bond n1 <--> n2.
    } else {
      dissolve(adjtet2); // Dissolve at n2.
      dummytet[0] = encode(adjtet2); // Let dummytet holds n2.
    }
    if (adjsh.sh != dummysh) {
      tsbond(adjtet2, adjsh); // Bond s <--> n2.
    }
    // Collapse deadtet.
    tetrahedrondealloc(deadtet.tet);
  }
  deadtetlist->clear();
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// deallocfaketets()    Deleted fake tets at fronts.                         //
//                                                                           //
// This routine is only called when the findrelocatepoint() routine fails.   //
// In other cases, the fake tets are removed automatically in carvecavity()  //
// or relocatepoint().                                                       //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::deallocfaketets(list* frontlist)
{
  triface front, neightet;
  face checksh;
  bool infectflag;
  int i;

  for (i = 0; i < frontlist->len(); i++) {
    // Get a front f.
    front = * (triface *)(* frontlist)[i];
    // Let f face inside C. (f is a face of tet adjacent to C).
    adjustedgering(front, CW);
    sym(front, neightet);
    tspivot(front, checksh);
    if (oppo(front) == (point) NULL) {
      if (b->verbose > 2) {
        //printf("    Get fake tet (%d, %d, %d).\n", pointmark(org(front)),
               //pointmark(dest(front)), pointmark(apex(front)));
      }
      if (neightet.tet != dummytet) {
        // The neightet may be infected. After dissolve it, the infect flag
        //   will be lost. Save the flag and restore it later.
        infectflag = infected(neightet);
        dissolve(neightet);
        if (infectflag) {
          infect(neightet);
        }
      }
      if (checksh.sh != dummysh) {
        infectflag = sinfected(checksh);
        stdissolve(checksh);
        if (infectflag) {
          sinfect(checksh);
        }
      }
      // Dealloc the 'fake' tet.
      tetrahedrondealloc(front.tet);
      // If 'neightet' is a hull face, let 'dummytet' bond to it. It is
      //   a 'dummytet' when this front was created from a new subface.
      //   In such case, it should not be bounded.
      if (neightet.tet != dummytet) {
        dummytet[0] = encode(neightet);
      }
    }
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// restorepolyhedron()    Restore the tetrahedralization in a polyhedron.    //
//                                                                           //
// This routine is only called when the operation of suppressing a point is  //
// aborted (eg., findrelocatepoint() routine fails). The polyhedron has been //
// remeshed by new tets. This routine restore the old tets in it.            //
//                                                                           //
// 'oldtetlist' contains the list of old tets. Each old tet t_o assumes that //
// it still connects to a tet t_b of the mesh, however, t_b does not connect //
// to t_o, this routine resets the connection such that t_b <--> t_o.        //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::restorepolyhedron(list* oldtetlist)
{
  triface oldtet, neightet, neineitet;
  face checksh;
  point *ppt;
  int i, j;

  for (i = 0; i < oldtetlist->len(); i++) {
    // Get an old tet t_o.
    oldtet = * (triface *)(* oldtetlist)[i];
    // Check the four sides of t_o.
    for (oldtet.loc = 0; oldtet.loc < 4; oldtet.loc++) {
      sym(oldtet, neightet);
      tspivot(oldtet, checksh);
      if (neightet.tet != dummytet) {
        //assert(!isdead(&neightet));  // SELF_CHECK  2009-07-24
        sym(neightet, neineitet);
        if (neineitet.tet != oldtet.tet) {
          // This face of t_o is a boundary of P.
          bond(neightet, oldtet);
          if (checksh.sh != dummysh) {
            tsbond(oldtet, checksh);
          }
        }
      } else {
        // t_o has a hull face. It should be the boundary of P.
        tsbond(oldtet, checksh);
        // Let dummytet[0] points to it.
        dummytet[0] = encode(oldtet);
      }
    }
    // Update the point-to-tet map.
    ppt = (point *) &(oldtet.tet[4]);
    for (j = 0; j < 4; j++) {
      setpoint2tet(ppt[j], encode(oldtet));
    }
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// suppressfacetpoint()    Suppress a point inside a facet.                  //
//                                                                           //
// The point p inside a facet F will be suppressed from F by either being    //
// deleted from the mesh or being relocated into the volume.                 //
//                                                                           //
// 'supsh' is a subface f of F, and p = sapex(f); the other parameters are   //
// working lists which are empty at the beginning and the end.               //
//                                                                           //
// 'optflag' is used for mesh optimization. If it is set, after removing p,  //
// test the object function on each new tet, queue bad tets.                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::suppressfacetpoint(face* supsh, list* frontlist,
  list* misfrontlist, list* ptlist, list* conlist, memorypool* viri,
  queue* flipque, bool noreloc, bool optflag)
{
  list *oldtetlist[2], *newtetlist[2];
  list *oldshlist, *newshlist;
  list *gluetetlist;
  list *glueshlist;
  triface oldtet, newtet, neightet;
  face oldsh, newsh;
  point suppt, newpt[2];
  point *cons, *ppt;
  enum interresult dir;
  REAL norm[3];
  bool success;
  int bakchecksubfaces;
  int shmark;
  int i, j;

  suppt = sapex(*supsh);
  if (b->verbose > 1) {
    //printf("    Suppress point %d in facet.\n", pointmark(suppt));
  }

  // Initialize working lists, variables.
  for (i = 0; i < 2; i++) {
    oldtetlist[i] = (list *) NULL;
    newtetlist[i] = (list *) NULL;
    newpt[i] = (point) NULL;
  }
  gluetetlist = new list(sizeof(triface), NULL, 256);
  glueshlist = new list(sizeof(face), NULL, 256);
  oldshlist = new list(sizeof(face), NULL, 256);
  newshlist = new list(sizeof(face), NULL, 256);
  success = true; // Assume p can be suppressed.

  bakchecksubfaces = checksubfaces;
  checksubfaces = 0;

  // Find subs of C(p).
  oldshlist->append(supsh);
  formstarpolygon(suppt, oldshlist, ptlist);
  // Get the edges of C(p). They form a closed polygon.
  for (i = 0; i < oldshlist->len(); i++) {
    oldsh = * (face *)(* oldshlist)[i];    
    cons = (point *) conlist->append(NULL);
    cons[0] = sorg(oldsh);
    cons[1] = sdest(oldsh);
  }
  // Re-triangulate the old C(p).
  shmark = shellmark(*supsh);
  triangulate(shmark, b->epsilon, ptlist, conlist, 0, NULL, viri, flipque);
  // Get new subs of C(p), remove protected segments.
  retrievenewsubs(newshlist, true);
  // Substitute the old C(p) with the new C(p)
  replacepolygonsubs(oldshlist, newshlist);
  // Clear work lists.
  ptlist->clear();
  conlist->clear();
  flipque->clear();
  viri->restart();

  checksubfaces = bakchecksubfaces;

  // B(p) (tets with p as a vertex) has been separated into two parts
  //   (B_0(p) and B_1(p)) by F. Process them individually.
  for (i = 0; i < 2 && success; i++) { 
    if (i == 1) sesymself(*supsh);
    // Get a tet containing p.
    stpivot(*supsh, oldtet);
    // Is this part empty?
    if (oldtet.tet == dummytet) continue;
    // Allocate spaces for storing (old and new) B_i(p).
    oldtetlist[i] = new list(sizeof(triface), NULL, 256);
    newtetlist[i] = new list(sizeof(triface), NULL, 256);
    // Form old B_i(p) in oldtetlist[i].
    //assert(!isdead(&oldtet));
    oldtetlist[i]->append(&oldtet);
    formstarpolyhedron(suppt, oldtetlist[i], ptlist, false);
    // Infect the tets in old B_i(p) (they're going to be delete).
    for (j = 0; j < oldtetlist[i]->len(); j++) {
      oldtet = * (triface *)(* (oldtetlist[i]))[j];
      infect(oldtet);
    }
    // Preparation for re-tetrahedralzing old B_i(p).
    orientnewsubs(newshlist, supsh, norm);
    // Tetrahedralize old B_i(p).
    success = constrainedcavity(&oldtet, newshlist, oldtetlist[i], ptlist,
      frontlist, misfrontlist, newtetlist[i], gluetetlist, glueshlist,
      flipque);
    // If p is not suppressed, do relocation if 'noreloc' is not set.
    if (!success && !noreloc) {
      // Try to relocate p into the old B_i(p).
      makepoint(&(newpt[i]));
      // success = findrelocatepoint(suppt, newpt[i], norm, frontlist,
      //                             oldtetlist[i]);
      success = findrelocatepoint2(suppt, newpt[i], norm, frontlist, 
                                   oldtetlist[i]);
      // Initialize newpt = suppt.
      // for (j = 0; j < 3; j++) newpt[i][j] = suppt[j];
      // success = smoothvolpoint(newpt[i], frontlist, true);
      if (success) {
        // p is relocated by newpt[i]. Now insert it. Don't do flip since
        //   the new tets may get deleted again.
        relocatepoint(newpt[i], &oldtet, frontlist, newtetlist[i], NULL);
        setpointtype(newpt[i], FREEVOLVERTEX);
        relverts++;
      } else {
        // Fail to relocate p. Clean fake tets and quit this option.
        deallocfaketets(frontlist);
        pointdealloc(newpt[i]);
        newpt[i] = (point) NULL;
        //assert(newtetlist[i]->len() == 0);
      }
    }
    if (!success && noreloc) {
      // Failed and no point relocation. Clean fake tets.
      deallocfaketets(frontlist);
    }
    // Clear work lists.
    ptlist->clear();
    frontlist->clear();
    misfrontlist->clear();
    // Do not clear gluetetlist. gluetetlist->clear();
    // Do not clear glueshlist.
    flipque->clear();
  }

  if (success) {
    // p has been removed! (Still in the pool).
    setpointtype(suppt, UNUSEDVERTEX);
    unuverts++;
    // Delete old C(p).
    for (i = 0; i < oldshlist->len(); i++) {
      oldsh = * (face *)(* oldshlist)[i];
      if (i == 0) {
        // Update the 'hullsize' if C(p) is on the hull.
        stpivot(oldsh, oldtet);
        if (oldtet.tet != dummytet) {
          sesymself(oldsh);
          stpivot(oldsh, oldtet);
        }
        if (oldtet.tet == dummytet) {
          // A boundary face. Update the 'hullsize'.
          j = oldshlist->len() - newshlist->len();
          //assert(j > 0);
          hullsize -= j;
        }
      }
      shellfacedealloc(subfaces, oldsh.sh);
    }
    // Delete old B_i(p).
    for (i = 0; i < 2; i++) {
      if (oldtetlist[i] != (list *) NULL) {
        // Delete tets of the old B_i(p).
        for (j = 0; j < oldtetlist[i]->len(); j++) {
          oldtet = * (triface *)(* (oldtetlist[i]))[j];
          //assert(!isdead(&oldtet));
          tetrahedrondealloc(oldtet.tet);
        }
      }
    }
    if (optflag) {
      // Check for new bad-quality tets.
      for (i = 0; i < 2; i++) {
        if (newtetlist[i] != (list *) NULL) {
          for (j = 0; j < newtetlist[i]->len(); j++) {
            newtet = * (triface *)(* (newtetlist[i]))[j];
            if (!isdead(&newtet)) checktet4opt(&newtet, true);
          }
        }
      }
    }
    // Insert outside new subfaces (if there are). 2009-07-08.
    for (i = 0; i < glueshlist->len(); i++) {
      newsh = * (face *)(* glueshlist)[i];
      // Insert it into mesh (it may be already inserted).
      newtet.tet = NULL;
      // The mesh may be non-convex (set convexflag = 0).
      dir = scoutsubface(&newsh, &newtet, 0);
      if (dir != SHAREFACE) {
        //assert(0);
      }
    }
  } else {
    // p is not suppressed. Recover the original state.
    unsupverts++;
    // Restore the old C(p).
    replacepolygonsubs(newshlist, oldshlist);
    // Delete subs of the new C(p)
    for (i = 0; i < newshlist->len(); i++) {
      newsh = * (face *)(* newshlist)[i];
      shellfacedealloc(subfaces, newsh.sh);
    }
    // Delete new subfaces in glueshlist. 2009-07-07
    for (i = 0; i < glueshlist->len(); i++) {
      newsh = * (face *)(* glueshlist)[i];
      for (j = 0; j < 2; j++) {
        stpivot(newsh, oldtet);
        if (oldtet.tet != dummytet) {
          tsdissolve(oldtet);
        }
        sesymself(newsh);
      }
      shellfacedealloc(subfaces, newsh.sh);
    }
    // Restore old B_i(p).
    for (i = 0; i < 2; i++) {
      if (oldtetlist[i] != (list *) NULL) {
        // Uninfect tets of old B_i(p).
        for (j = 0; j < oldtetlist[i]->len(); j++) {
          oldtet = * (triface *)(* (oldtetlist[i]))[j];
          //assert(infected(oldtet));
          uninfect(oldtet);
        }
        // Has it been re-meshed?
        // if (newtetlist[i]->len() > 0) {
          // Restore the old B_i(p).
          restorepolyhedron(oldtetlist[i]);
          // Delete tets of the new B_i(p);
          for (j = 0; j < newtetlist[i]->len(); j++) {
            newtet = * (triface *)(* (newtetlist[i]))[j];
            // Some new tets may already be deleted (by carvecavity()).
            if (!isdead(&newtet)) {
              tetrahedrondealloc(newtet.tet);
            }
          }
        // }
        // Dealloc newpt[i] if it exists.
        if (newpt[i] != (point) NULL) {
          pointdealloc(newpt[i]);
          relverts--;
        }
      }
    }
    // Detach new subfaces attached to glue tets. 2009-07-10.
    for (i = 0; i < gluetetlist->len(); i++) {
      oldtet = * (triface *)(* gluetetlist)[i];
      if (!isdead(&oldtet)) {
        // It contains a new subface which has already been deleted (in above).
        tspivot(oldtet, newsh);
        //assert(isdead(&newsh));
        tsdissolve(oldtet);
        sym(oldtet, neightet);
        if (neightet.tet != dummytet) {
          tsdissolve(neightet);
        }
      }
    }
    // Update the point-to-subface map. 2009-07-22.
    for (i = 0; i < oldshlist->len(); i++) {
      oldsh = * (face *)(* oldshlist)[i];
      ppt = (point *) &(oldsh.sh[3]);
      for (j = 0; j < 3; j++) {
        setpoint2sh(ppt[j], sencode(oldsh));
      }
    }
  }

  // Delete work lists.
  delete oldshlist;
  delete newshlist;
  for (i = 0; i < 2; i++) {
    if (oldtetlist[i] != (list *) NULL) {
      delete oldtetlist[i];
      delete newtetlist[i];
    }
  }
  delete gluetetlist;
  delete glueshlist;

  return success;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// suppresssegpoint()    Suppress a point on a segment.                      //
//                                                                           //
// The point p on a segment S will be suppressed from S by either being      //
// deleted from the mesh or being relocated into the volume.                 //
//                                                                           //
// 'supseg' is the segment S, and p = sdest(S); the other parameters are     //
// working lists which are empty at the beginning and the end.               //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::suppresssegpoint(face* supseg, list* spinshlist,
  list* newsegshlist, list* frontlist, list* misfrontlist, list* ptlist,
  list* conlist, memorypool* viri, queue* flipque, bool noreloc, bool optflag)
{
  list **oldtetlist, **newtetlist;
  list **oldshlist, **newshlist;
  list *pnewshlist, *dnewshlist;
  list *gluetetlist;
  list *glueshlist;
  triface oldtet, newtet, neightet, spintet;
  face oldsh, newsh, *worksharray;
  face startsh, spinsh, segsh1, segsh2;
  face nsupseg, newseg, prevseg, nextseg;
  point suppt, *newpt;
  point pa, pb, pc, pd, *cons, *ppt;
  enum interresult dir;
  REAL pnorm[2][3], norm[3], len;
  bool success;
  int bakchecksubfaces;
  int shmark;
  int n, i, j, k;

  // Get the Steiner point p.
  //assert(supseg->shver < 2);
  suppt = sdest(*supseg);
  // Find the segment ab split by p.
  senext(*supseg, nsupseg);
  spivotself(nsupseg);
  //assert(nsupseg.sh != dummysh);
  nsupseg.shver = 0;
  if (sorg(nsupseg) != suppt) sesymself(nsupseg);
  //assert(sorg(nsupseg) == suppt);
  pa = sorg(*supseg);
  pb = sdest(nsupseg);
  if (b->verbose > 1) {
    //printf("    Remove point %d on segment (%d, %d).\n",
           //pointmark(suppt), pointmark(pa), pointmark(pb));
  }

  // Let startsh s containing p.
  spivot(*supseg, startsh);
  spinsh = startsh;
  do {
    // Adjust spinsh to be the edge [pa, suppt].
    findedge(&spinsh, pa, suppt);
    // Save it in list.
    spinshlist->append(&spinsh);
    // Go to the next facet.
    spivotself(spinsh);
    if (spinsh.sh == dummysh) break;
  } while (spinsh.sh != startsh.sh);

  n = spinshlist->len();

  if (n > 2) {
    // Order the subfaces to be counterclockwise around edge [pa, suppt].
    worksharray = new face[n];  // Temporarily use it.
    for (i = 0; i < n; i++) {
      worksharray[i] = * (face *)(* spinshlist)[i];
      sinfect(worksharray[i]);
    }
    spinshlist->clear(); // Clear the list for the re-ordering.
    for (i = 0; i < n; i++) {
      worksharray[i] = * (face *)(* spinshlist)[i];
      if (sinfected(worksharray[i])) {
        // Collect subfaces at this segment.
        startsh = worksharray[i];
        stpivot(startsh, neightet);
        if (neightet.tet == dummytet) {
          sesymself(startsh);
          stpivot(startsh, neightet);
          //assert(neightet.tet != dummytet);
        }
        // Adjust neightet to be the edge [pa, suppt].
        findedge(&neightet, pa, suppt);
        // Adjust neightet to be the boundary face (if there exists).
        spintet = neightet;
        while (1) {
          if (!fnextself(spintet)) {
            esymself(spintet);
            break;
          }
          if (apex(spintet) == apex(neightet)) break;
        }
        // Start from spintet, collect all subfaces at this segment.
        neightet = spintet;
        pc = org(spintet); 
        pd = dest(spintet); 
        // [pc, pd] is the rotating edge (axis). It may be either 
        //   [pa, suppt] or [suppt, pa].
        while (1) {
          tspivot(spintet, spinsh);
          if (spinsh.sh != dummysh) {
            //assert(sinfected(spinsh));
            suninfect(spinsh);
            // Let spinsh be the same oriented edge as spintet.
            findedge(&spinsh, pc, pd);
            spinshlist->append(&spinsh);
          }
          if (!fnextself(spintet)) break;
          if (apex(spintet) == apex(neightet)) break;
        }
      }
      //assert(!sinfected(worksharray[i]));
    } // i
    delete [] worksharray;
  }
  
  if (spinshlist->len() == 1) {
    // This case has not handled yet.
    // //printf("Unhandled case: segment only belongs to one facet.\n");
    spinshlist->clear();
    unsupverts++;
    return false;
  }

  // Suppose ab is shared by n facets (n > 1), then there are n B(p) (tets
  //   with p as a vertex). Some B(p) may be empty, eg, outside.
  // n = spinshlist->len();
  oldtetlist = new list*[n];
  newtetlist = new list*[n];
  oldshlist = new list*[n];
  newshlist = new list*[n];
  newpt = new point[n];
  for (i = 0; i < n; i++) {
    oldtetlist[i] = (list *) NULL;
    newtetlist[i] = (list *) NULL;
    oldshlist[i] = (list *) NULL;
    newshlist[i] = (list *) NULL;
    newpt[i] = (point) NULL;
  }
  gluetetlist = new list(sizeof(triface), NULL, 256);
  glueshlist = new list(sizeof(face), NULL, 256);

  // Create a new segment ab (result in newseg).
  makeshellface(subsegs, &newseg);
  setsorg(newseg, pa);
  setsdest(newseg, pb);
  // ab gets the same mark and segment type as ap.
  setshellmark(newseg, shellmark(*supseg));
  setshelltype(newseg, shelltype(*supseg));
  if (b->quality && varconstraint) {
    // Copy the areabound into the new subsegment.
    setareabound(newseg, areabound(*supseg));
  }
  // Save the old connection at a.
  senext2(*supseg, prevseg);
  spivotself(prevseg);
  if (prevseg.sh != dummysh) {
    prevseg.shver = 0;
    if (sdest(prevseg) != pa) sesymself(prevseg);
    //assert(sdest(prevseg) == pa);
    senextself(prevseg);
    senext2self(newseg);
    sbond(newseg, prevseg);
    newseg.shver = 0;
  }
  // Save the old connection at b.
  senext(nsupseg, nextseg);
  spivotself(nextseg);
  if (nextseg.sh != dummysh) {
    nextseg.shver = 0;
    if (sorg(nextseg) != pb) sesymself(nextseg);
    //assert(sorg(nextseg) == pb);
    senext2self(nextseg);
    senextself(newseg);
    sbond(newseg, nextseg);
    newseg.shver = 0;
  }

  bakchecksubfaces = checksubfaces;
  checksubfaces = 0;

  // Re-triangulate C(p) (subs with p as a vertex) to remove p.
  for (i = 0; i < spinshlist->len(); i++) {
    spinsh = * (face *)(* spinshlist)[i];
    // Allocate spaces for C_i(p).
    oldshlist[i] = new list(sizeof(face), NULL, 256);
    newshlist[i] = new list(sizeof(face), NULL, 256);
    // Get the subs of C_i(p).
    oldshlist[i]->append(&spinsh);
    formstarpolygon(suppt, oldshlist[i], ptlist);
    // Find the edges of C_i(p). It DOES NOT form a closed polygon.
    for (j = 0; j < oldshlist[i]->len(); j++) {
      oldsh = * (face *)(* (oldshlist[i]))[j];    
      cons = (point *) conlist->append(NULL);
      cons[0] = sorg(oldsh);
      cons[1] = sdest(oldsh);
    }
    // The C_i(p) isn't closed without ab. Add it to it.
    cons = (point *) conlist->append(NULL);
    cons[0] = pa;
    cons[1] = pb;
    // Re-triangulate C_i(p).
    shmark = shellmark(spinsh);
    triangulate(shmark, b->epsilon, ptlist, conlist, 0, NULL, viri, flipque);
    // Get new subs of C_i(p), remove protected segments.
    retrievenewsubs(newshlist[i], true);
    // Substitute old C_i(p) with the new C_i(p). !IT IS NOT COMPLETE!
    replacepolygonsubs(oldshlist[i], newshlist[i]);
    // Find the new subface s having edge ab.
    for (j = 0; j < newshlist[i]->len(); j++) {
      segsh1 = * (face *)(* (newshlist[i]))[j];
      for (k = 0; k < 3; k++) {
        if (((sorg(segsh1) == pa) && (sdest(segsh1) == pb)) ||
            ((sorg(segsh1) == pb) && (sdest(segsh1) == pa))) break;
        senextself(segsh1);
      }
      if (k < 3) break; // Found.
    }
    //assert(j < newshlist[i]->len()); // ab must exist.
    // Bond s and ab together. The C_i(p) is completedly substituted.
    ssbond(segsh1, newseg);
    // Save s for forming the face ring of ab.
    newsegshlist->append(&segsh1);
    // Clear work lists.
    ptlist->clear();
    conlist->clear();
    flipque->clear();
    viri->restart();
  }
  // Form the face ring of ab.
  for (i = 0; i < newsegshlist->len(); i++) {
    segsh1 = * (face *)(* newsegshlist)[i];
    if ((i + 1) == newsegshlist->len()) {
      segsh2 = * (face *)(* newsegshlist)[0];
    } else {
      segsh2 = * (face *)(* newsegshlist)[i + 1];
    }
    sbond1(segsh1, segsh2);
  }

  checksubfaces = bakchecksubfaces;

  // A work list for keeping subfaces from two facets.
  dnewshlist = new list(sizeof(face), NULL, 256);
  success = true; // Assume p is suppressable.

  // Suppress p in all B(p). B_i(p) is looped wrt the right-hand rule of ab.
  for (i = 0; i < spinshlist->len() && success; i++) {
    // Get an old  subface s (ap) of a facet.
    spinsh = * (face *)(* spinshlist)[i];
    // // Let the edge direction of s be a->b. Hence all subfaces follow
    // //   the right-hand rule of ab.
    // if (sorg(spinsh) != pa) sesymself(spinsh);
    // spinsh has been directed. Do not change its orientation now.
    // Get a tet t of B_i(p).
    stpivot(spinsh, oldtet);
    // Is B_i(p) empty?
    if (oldtet.tet == dummytet) continue;
    // Allocate spaces for B_i(p).
    oldtetlist[i] = new list(sizeof(triface), NULL, 256);
    newtetlist[i] = new list(sizeof(triface), NULL, 256);
    // Find all tets of old B_i(p).
    oldtetlist[i]->append(&oldtet);
    formstarpolyhedron(suppt, oldtetlist[i], ptlist, false);
    // Infect tets of old B_i(p) (they're going to be deleted).
    for (j = 0; j < oldtetlist[i]->len(); j++) {
      oldtet = * (triface *)(* (oldtetlist[i]))[j];
      infect(oldtet);
    }
    // Collect new subfaces (of two facets) bounded B_i(p).
    for (k = 0; k < 2; k++) {
      if ((i + k) < spinshlist->len()) {
        pnewshlist = newshlist[i + k];
        segsh1 = * (face *)(* spinshlist)[i + k];
      } else {
        pnewshlist = newshlist[0];
        segsh1 = * (face *)(* spinshlist)[0];
      }
      /*// Adjust the orientation of segsh1 to face to the inside of C.
      if (k == 0) {
        if (sorg(segsh1) != pa) sesymself(segsh1);
        //assert(sorg(segsh1) == pa);
      } else {
        if (sdest(segsh1) != pa) sesymself(segsh1);
        //assert(sdest(segsh1) == pa);
      }*/
      if (k == 0) {
        // segsh1 has already been directed pointing to the inside of C.
      } else {
        // Reverse the direction of segsh1.
        sesymself(segsh1);
      }
      //   its orientation now.
      // Preparation for re-tetrahedralzing old B_i(p).
      orientnewsubs(pnewshlist, &segsh1, pnorm[k]);
      for (j = 0; j < pnewshlist->len(); j++) {
        dnewshlist->append((face *)(* pnewshlist)[j]);
      }
    }
    // Tetrahedralize B_i(p).
    success = constrainedcavity(&oldtet, dnewshlist, oldtetlist[i], ptlist,
      frontlist, misfrontlist, newtetlist[i], gluetetlist, glueshlist,
      flipque);
    if (!success && !noreloc) {
      // C must be finished by re-locating the steiner point.
      makepoint(&(newpt[i]));
      for (j = 0; j < 3; j++) norm[j] = 0.5 * (pnorm[0][j] + pnorm[1][j]);
      // Normialize the normal.
      len = sqrt(norm[0] * norm[0] + norm[1] * norm[1] + norm[2] * norm[2]);
      //assert(len != 0);
      for (j = 0; j < 3; j++) norm[j] /= len;
      // success = findrelocatepoint(suppt, newpt[i], norm, frontlist,
      //                             oldtetlist[i]);
      success = findrelocatepoint2(suppt, newpt[i], norm, frontlist, 
                                   oldtetlist[i]);
      // success = findrelocatepoint3(suppt, pa, pb, newpt[i], norm, frontlist,
      //                              oldtetlist[i]);
      // for (j = 0; j < 3; j++) newpt[i][j] = suppt[j];
      // success = smoothvolpoint(newpt[i], frontlist, true);
      if (success) {
        // p is relocated by newpt[i]. Now insert it. Don't do flip since
        //   the new tets may get deleted again.
        if (relocatepoint(newpt[i], &oldtet, frontlist, newtetlist[i], NULL)) {
          setpointtype(newpt[i], FREEVOLVERTEX);
          relverts++;
        } else {
          // The faked tets are deleted in above route.
          pointdealloc(newpt[i]);
          newpt[i] = (point) NULL;
          newtetlist[i]->clear();
          success = false;
        }
      } else {
        // Fail to relocate p. Clean fake tets and quit this option.
        deallocfaketets(frontlist);
        pointdealloc(newpt[i]);
        newpt[i] = (point) NULL;
        //assert(newtetlist[i]->len() == 0);
      }
    }
    if (!success && noreloc) {
      // Failed and no point relocation. Clean fake tets.
      deallocfaketets(frontlist);
    }
    // Clear work lists.
    dnewshlist->clear();
    ptlist->clear();
    frontlist->clear();
    misfrontlist->clear();
    // Do not clear gluetetlist. // gluetetlist->clear();
    // Do not clear glueshlist.
    flipque->clear();
  }

  if (success) {
    // p has been suppressed. (Still in the pool).
    setpointtype(suppt, UNUSEDVERTEX);
    unuverts++;
    // Update the point-to-seg map.
    setpoint2seg(pa, sencode(newseg));
    setpoint2seg(pb, sencode(newseg));
    // Delete old segments ap, pb.
    shellfacedealloc(subsegs, supseg->sh);
    shellfacedealloc(subsegs, nsupseg.sh);
    // Delete subs of old C_i(p).
    for (i = 0; i < spinshlist->len(); i++) {
      for (j = 0; j < oldshlist[i]->len(); j++) {
        oldsh = * (face *)(* (oldshlist[i]))[j];
        if (j == 0) {
          // Update 'hullsize' if C_i(p) is on the hull.
          stpivot(oldsh, oldtet);
          if (oldtet.tet != dummytet) {
            sesymself(oldsh);
            stpivot(oldsh, oldtet);
          }
          if (oldtet.tet == dummytet) {
            // Update 'hullsize'.
            k = oldshlist[i]->len() - newshlist[i]->len();
            //assert(k > 0);
            hullsize -= k;
          }
        }
        shellfacedealloc(subfaces, oldsh.sh);
      }
    }
    // Delete tets old B_i(p).
    for (i = 0; i < spinshlist->len(); i++) {
      // Delete them if it is not empty.
      if (oldtetlist[i] != (list *) NULL) {
        for (j = 0; j < oldtetlist[i]->len(); j++) {
          oldtet = * (triface *)(* (oldtetlist[i]))[j];
          //assert(!isdead(&oldtet));
          tetrahedrondealloc(oldtet.tet);
        }
      }
    }
    if (optflag) {
      for (i = 0; i < spinshlist->len(); i++) {
        // Check for new bad-quality tets.
        if (newtetlist[i] != (list *) NULL) {
          for (j = 0; j < newtetlist[i]->len(); j++) {
            newtet = * (triface *)(* (newtetlist[i]))[j];
            if (!isdead(&newtet)) checktet4opt(&newtet, true);
          }
        }
      }
    }
    // Insert outside new subfaces (if there are). 2009-07-08.
    for (i = 0; i < glueshlist->len(); i++) {
      newsh = * (face *)(* glueshlist)[i];
      // Insert it into mesh (it may be already inserted).
      newtet.tet = NULL;
      // The mesh may be non-convex (set convexflag = 0).
      dir = scoutsubface(&newsh, &newtet, 0);
      if (dir != SHAREFACE) {
        //assert(0);
      }
    }
  } else {
    // p is not suppressed. Recover the original state.
    unsupverts++;
    // Restore old connection at a.
    senext2(*supseg, prevseg);
    spivotself(prevseg);
    if (prevseg.sh != dummysh) {
      prevseg.shver = 0;
      if (sdest(prevseg) != pa) sesymself(prevseg);
      //assert(sdest(prevseg) == pa);
      senextself(prevseg);
      senext2self(*supseg);
      sbond(*supseg, prevseg);
      senextself(*supseg); // Restore original state.
      //assert(supseg->shver < 2);
    }
    // Restore old connection at b.
    senext(nsupseg, nextseg);
    spivotself(nextseg);
    if (nextseg.sh != dummysh) {
      nextseg.shver = 0;
      if (sorg(nextseg) != pb) sesymself(nextseg);
      //assert(sorg(nextseg) == pb);
      senext2self(nextseg);
      senextself(nsupseg);
      sbond(nsupseg, nextseg);
      // nsupseg.shver = 0;
      senext2self(nsupseg); // Restore original state
      //assert(nsupseg.shver < 2);
    }
    // Delete the new segment ab.
    shellfacedealloc(subsegs, newseg.sh);
    // Restore old C_i(p).
    for (i = 0; i < spinshlist->len(); i++) {
      replacepolygonsubs(newshlist[i], oldshlist[i]);
      // Delete subs of the new C_i(p)
      for (j = 0; j < newshlist[i]->len(); j++) {
        newsh = * (face *)(* (newshlist[i]))[j];
        shellfacedealloc(subfaces, newsh.sh);
      }
    }
    // Delete new subfaces in glueshlist. 2009-07-07.
    for (i = 0; i < glueshlist->len(); i++) {
      newsh = * (face *)(* glueshlist)[i];
      if (!isdead(&newsh)) {
        // Disconnect adjacent tets.
        for (j = 0; j < 2; j++) {
          stpivot(newsh, oldtet);
          if (oldtet.tet != dummytet) {
            tsdissolve(oldtet);
          }
          sesymself(newsh);
        }
        shellfacedealloc(subfaces, newsh.sh);
      }
    }
    // Restore old B_i(p).
    for (i = 0; i < spinshlist->len(); i++) {
      if (oldtetlist[i] != (list *) NULL) {
        // Uninfect tets of old B_i(p).
        for (j = 0; j < oldtetlist[i]->len(); j++) {
          oldtet = * (triface *)(* (oldtetlist[i]))[j];
          //assert(infected(oldtet));
          uninfect(oldtet);
        }
        // Has it been re-meshed?
        // if (newtetlist[i]->len() > 0) {
          // Restore the old B_i(p).
          restorepolyhedron(oldtetlist[i]);
          // Delete tets of the new B_i(p);
          for (j = 0; j < newtetlist[i]->len(); j++) {
            newtet = * (triface *)(* (newtetlist[i]))[j];
            // Some new tets may already be deleted (by carvecavity()).
            if (!isdead(&newtet)) {
              tetrahedrondealloc(newtet.tet);
            }
          }
        // }
        // Dealloc newpt[i] if it exists.
        if (newpt[i] != (point) NULL) {
          pointdealloc(newpt[i]);
          relverts--;
        }
      }
    }
    // Detach new subfaces attached to glue tets. 2009-07-10.
    for (i = 0; i < gluetetlist->len(); i++) {
      oldtet = * (triface *)(* gluetetlist)[i];
      if (!isdead(&oldtet)) {
        // It contains a new subface which has already been deleted (in above).
        tspivot(oldtet, newsh);
        //assert(isdead(&newsh));
        tsdissolve(oldtet);
        sym(oldtet, neightet);
        if (neightet.tet != dummytet) {
          tsdissolve(neightet);
        }
      }
    }
    // Update the point-to-subface map. 2009-07-22.
    for (i = 0; i < spinshlist->len(); i++) {
      for (j = 0; j < oldshlist[i]->len(); j++) {
        oldsh = * (face *)(* oldshlist[i])[j];
        ppt = (point *) &(oldsh.sh[3]);
        for (k = 0; k < 3; k++) {
          setpoint2sh(ppt[k], sencode(oldsh));
        }
      }
    }
  }

  // Delete work lists.
  delete [] newpt; // BUG fixed. Thanks dmyan, June 23, 2007.
  delete dnewshlist;
  for (i = 0; i < spinshlist->len(); i++) {
    delete oldshlist[i];
    delete newshlist[i];
  } 
  delete [] oldshlist;
  delete [] newshlist;
  for (i = 0; i < spinshlist->len(); i++) {
    if (oldtetlist[i] != (list *) NULL) {
      delete oldtetlist[i];
      delete newtetlist[i];
    }
  }
  delete [] oldtetlist;
  delete [] newtetlist;
  delete gluetetlist;
  delete glueshlist;
  // Clear work lists.
  newsegshlist->clear();
  spinshlist->clear();

  return success;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// suppressvolpoint()    Suppress a point inside mesh.                       //
//                                                                           //
// The point p = org(suptet) is inside the mesh and will be suppressed from  //
// the mesh. Note that p may not be suppressed.                              //
//                                                                           //
// 'optflag' is used for mesh optimization. If it is set, after removing p,  //
// test the object function on each new tet, queue bad tets.                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::suppressvolpoint(triface* suptet, list* frontlist,
  list* misfrontlist, list* ptlist, queue* flipque, bool optflag)
{
  list *myfrontlist, *mymisfrontlist, *myptlist;
  list *oldtetlist, *newtetlist;
  list *gluetetlist;
  list *glueshlist;
  list *newshlist; // a dummy list.
  queue *myflipque;
  triface oldtet, newtet;
  point suppt, conpt, *ppt;
  bool success;
  int j, k;

  // Allocate spaces for storing (old and new) B(p).
  oldtetlist = new list(sizeof(triface), NULL, 256);
  newtetlist = new list(sizeof(triface), NULL, 256);
  gluetetlist = new list(sizeof(triface), NULL, 256);
  glueshlist = new list(sizeof(face), NULL, 256);
  newshlist = new list(sizeof(face), NULL, 256);
  // Allocate work lists if user doesn't supply them.
  myfrontlist = mymisfrontlist = myptlist = (list *) NULL;
  myflipque = (queue *) NULL;
  if (frontlist == (list *) NULL) {
    myfrontlist = new list(sizeof(triface), NULL, 256);
    frontlist = myfrontlist;
    mymisfrontlist = new list(sizeof(triface), NULL, 256);
    misfrontlist = mymisfrontlist;
    myptlist = new list(sizeof(point *), NULL, 256);
    ptlist = myptlist;
    myflipque = new queue(sizeof(badface));
    flipque = myflipque;
  }

  suppt = org(*suptet);
  oldtet = *suptet;
  success = true; // Assume p can be suppressed.

  if (b->verbose > 1) {
    //printf("    Remove point %d in mesh.\n", pointmark(suppt));
  }

  // Form old B(p) in oldtetlist.
  oldtetlist->append(&oldtet);
  formstarpolyhedron(suppt, oldtetlist, ptlist, false);
  // Infect the tets in old B(p) (they're going to be delete).
  for (j = 0; j < oldtetlist->len(); j++) {
    oldtet = * (triface *)(* oldtetlist)[j];
    infect(oldtet);
  }
  // Tetrahedralize old B(p).
  success = constrainedcavity(&oldtet, newshlist, oldtetlist, ptlist,
     frontlist, misfrontlist, newtetlist, gluetetlist, glueshlist, flipque);
  if (!success) {
    // Unable to suppress p.
    deallocfaketets(frontlist);
    // Try to collapse an edge at p. 
    conpt = (point) NULL;
    //assert(newtetlist->len() == 0);
    if (findcollapseedge(suppt, &conpt, oldtetlist, ptlist)) {
      // Collapse the edge suppt->conpt. Re-use newtetlist.
      collapseedge(suppt, conpt, oldtetlist, newtetlist);
      // The oldtetlist contains newtetlist.
      if (optflag) {
        //assert(newtetlist->len() == 0);
        for (j = 0; j < oldtetlist->len(); j++) {
          newtet = * (triface *)(* oldtetlist)[j];
          newtetlist->append(&newtet);
        }
      }
      oldtetlist->clear(); // Do not delete them.
      collapverts++;
      success = true;
    }
  }
  if (success) {
    // p has been removed! (Still in the pool).
    setpointtype(suppt, UNUSEDVERTEX);
    unuverts++;
    suprelverts++;
    // Delete old B(p).
    for (j = 0; j < oldtetlist->len(); j++) {
      oldtet = * (triface *)(* oldtetlist)[j];
      //assert(!isdead(&oldtet));
      tetrahedrondealloc(oldtet.tet);
    }
    if (optflag) {
      // Check for new bad tets.
      for (j = 0; j < newtetlist->len(); j++) {
        newtet = * (triface *)(* newtetlist)[j];
        if (!isdead(&newtet)) checktet4opt(&newtet, true);
      }
    }
  } else {
    // p is not suppressed. Recover the original state.
    // Uninfect tets of old B(p).
    for (j = 0; j < oldtetlist->len(); j++) {
      oldtet = * (triface *)(* oldtetlist)[j];
      //assert(infected(oldtet));
      uninfect(oldtet);
      // Update the point-to-tet map.
      ppt = (point *) &(oldtet.tet[4]);
      for (k = 0; k < 4; k++) {
        setpoint2tet(ppt[k], encode(oldtet));
      }
    }
  }

  // Clear work lists.
  ptlist->clear();
  frontlist->clear();
  misfrontlist->clear();
  gluetetlist->clear();
  glueshlist->clear();
  flipque->clear();
  // Deallocate work lists.
  if (myfrontlist != (list *) NULL) {
    delete myfrontlist;
    delete mymisfrontlist;
    delete myptlist;
    delete myflipque;
  }
  delete oldtetlist;
  delete newtetlist;
  delete gluetetlist;
  delete glueshlist;
  delete newshlist;

  return success;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// removesteiners2()    Remove Steiner points.                               //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::removesteiners2()
{
  list *frontlist, *misfrontlist;
  list *spinshlist, *newsegshlist;
  list *ptlist, *conlist;
  memorypool *viri;
  queue *flipque;
  triface searchtet, checktet;
  face searchsh;
  face searchseg;
  point pa, pt;
  enum verttype vtype;
  bool remflag, success; //, optflag;
  int oldnum, rmstein;
  int unsupbdrycount;  // Count the unsuppressed boundary Steiner points.
  int iter, i, j;

  if (!b->quiet) {
    //printf("Removing Steiner points.\n");
  }

  // Initialize work lists.
  frontlist = new list(sizeof(triface), NULL);
  misfrontlist = new list(sizeof(triface), NULL);
  spinshlist = new list(sizeof(face), NULL);
  newsegshlist = new list(sizeof(face), NULL);
  ptlist = new list(sizeof(point *), NULL);
  conlist = new list(sizeof(point *) * 2, NULL);
  flipque = new queue(sizeof(badface));
  viri = new memorypool(sizeof(shellface *), 1024, POINTER, 0);

  caveshlist = new arraypool(sizeof(face), 10);
  caveshbdlist = new arraypool(sizeof(face), 10);

  oldnum = unuverts;
  relverts = suprelverts = collapverts = unsupverts;

  iter = 0;
  i = 0;

  do { // iter
    unsupbdrycount = 0;
    // Initialize the two arrays (global values).
    fixededgelist = new arraypool(sizeof(point) * 2, 8);
    elemfliplist = new arraypool(sizeof(elemflip), 8);

    do { // i
      rmstein = unuverts;
      points->traversalinit();
      pa = pointtraverse();
      while (pa != NULL) {
        j = pointmark(pa) - in->firstnumber;
        if (j >= in->numberofpoints) {
          // pa is not an input points.
          vtype = pointtype(pa);
          if ((vtype == FREESEGVERTEX) || (vtype == FREESUBVERTEX) ||
              (vtype == FREEVOLVERTEX)) {
            i++;
            if (b->verbose > 1) {
              //printf(" Removing %d-th Steiner point %d.\n", i, pointmark(pa));
            }
          }
          if (vtype == FREESEGVERTEX) {
            remflag = false;
            // pa is not an input point.
            if (b->nobisect == 1) {
              point2segorg(pa, searchseg);
              sstpivot(&searchseg, &checktet);
              //assert(checktet.tet != dummytet);
              pt = apex(checktet);
              do {
                if (!fnextself(checktet)) {
                  // Meet a boundary face - p is on the hull.
                  remflag = true; 
                  break;
                }
              } while (apex(checktet) != pt);
            } else {
              // '-YY'. Remove p whatever s is a hull face or not.
              remflag = true;
            }
            if (remflag) {
              point2segorg(pa, searchseg);
              sesymself(searchseg); // pa = sdest();
              success = suppresssegpoint(&searchseg, spinshlist, newsegshlist,
                frontlist, misfrontlist, ptlist, conlist, viri, flipque, 
                false, false);
            }
          } else if (vtype == FREESUBVERTEX) {
            remflag = false;
            // pa is not an input point.
            if (b->nobisect == 1) {
              // '-Y'. Remove p if s is a hull face.
              point2shorg(pa, searchsh);
              stpivot(searchsh, checktet);
              if (checktet.tet != dummytet) {
                sesymself(searchsh);
                stpivot(searchsh, checktet);
              }
              remflag = (checktet.tet == dummytet);
            } else {
              // '-YY'. Remove p whatever s is a hull face or not.
              remflag = true;
            }
            if (remflag) {
              point2shorg(pa, searchsh);
              senextself(searchsh); // pa = sapex();
              success = suppressfacetpoint(&searchsh, frontlist, misfrontlist, 
                ptlist, conlist, viri, flipque, false, false);
            }
          } else if (vtype == FREEVOLVERTEX) {
            // pa is not an input point.
            point2tetorg(pa, searchtet);
            success = suppressvolpoint(&searchtet, frontlist, misfrontlist, 
              ptlist, flipque, false);
          }
        } // if (j >= in->numberofpoints)
        pa = pointtraverse();
      }
      // Continue if any Steiner point has been removed.
    } while (unuverts > rmstein);

    delete fixededgelist;
    delete elemfliplist;
    fixededgelist = NULL;
    elemfliplist = NULL;

    if (b->optlevel > 0) { // b->optlevel is set by -s.
      // Improve the local mesh quality at relocated Steiner points.
      b_steinerflag = true;
      optimizemesh2(true);
      b_steinerflag = false;
      
      // Smooth the relocated vertices (also count unsupressed vertices).
      points->traversalinit();
      pa = pointtraverse();
      while (pa != NULL) {
        j = pointmark(pa) - in->firstnumber;
        if (j >= in->numberofpoints) {
          // pa is not an input point.
          vtype = pointtype(pa);
          if (vtype == FREEVOLVERTEX) {
            point2tetorg(pa, searchtet);
            frontlist->append(&searchtet);
            formstarpolyhedron(pa, frontlist, NULL, false);
            smoothpoint(pa, NULL, NULL, frontlist, false, NULL);
            frontlist->clear();
          } else if (vtype == FREESEGVERTEX) {
            remflag = false;
            // pa is not an input point.
            if (b->nobisect == 1) {
              point2segorg(pa, searchseg);
              sstpivot(&searchseg, &checktet);
              //assert(checktet.tet != dummytet);
              pt = apex(checktet);
              do {
                if (!fnextself(checktet)) {
                  // Meet a boundary face - p is on the hull.
                  remflag = true; 
                  break;
                }
              } while (apex(checktet) != pt);
            } else {
              // '-YY'. Remove p whatever s is a hull face or not.
              remflag = true;
            }
            if (remflag) {
              unsupbdrycount++;
            }
          } else if (vtype == FREESUBVERTEX) {
            remflag = false;
            // pa is not an input point.
            if (b->nobisect == 1) {
              // '-Y'. Remove p if s is a hull face.
              point2shorg(pa, searchsh);
              stpivot(searchsh, checktet);
              if (checktet.tet != dummytet) {
                sesymself(searchsh);
                stpivot(searchsh, checktet);
              }
              remflag = (checktet.tet == dummytet);
            } else {
              // '-YY'. Remove p whatever s is a hull face or not.
              remflag = true;
            }
            if (remflag) {
              unsupbdrycount++;
            }
          }
        }
        pa = pointtraverse();
      }
    }

    if (unsupbdrycount == 0) {
      break; // No unsupressed boundary points left.
    }
    iter++;
  } while ((b->optlevel > 0) && (iter < b->optpasses)); 
  // Comment: default b->optpasses is 3, it can be set by -ss option. 

  if (b->verbose > 0) {
    //printf("  %d points removed from boundary.\n", unuverts - oldnum);
    // if (relverts > 0) {
      //printf("  %d points relocated (%d suppressed, %d collapsed).\n",
             //relverts, suprelverts - collapverts, collapverts);
      if (unsupverts > 0) {
        //printf("  %d points were unsuppressed.\n", unsupverts);
      }
      if (unsupbdrycount > 0) {
        //printf("  !! %d points remain in the boundary.\n", unsupbdrycount);
      }
      //printf("  %d points remain in the interior.\n", relverts-suprelverts);
    // }
  }

  /*// DEBUG Dump extremly bad tets.
  badtetrahedrons = new memorypool(sizeof(badface), ELEPERBLOCK, POINTER, 0);
  cosmaxdihed = cos(179.999 * PI / 180.0);
  cosmindihed = cos(0.1 * PI / 180.0);
  tallslivers(true);
  dumpbadtets();
  delete badtetrahedrons;
  badtetrahedrons = NULL;
  // DEBUG END */

  delete caveshlist;
  delete caveshbdlist;
  caveshlist = NULL;
  caveshbdlist = NULL;

  // Delete work lists.
  delete frontlist;
  delete misfrontlist;
  delete spinshlist;
  delete newsegshlist;
  delete ptlist;
  delete conlist;
  delete flipque;
  delete viri;
}

////                                                                       ////
////                                                                       ////
//// steiner_cxx //////////////////////////////////////////////////////////////

//// reconstruct_cxx //////////////////////////////////////////////////////////
////                                                                       ////
////                                                                       ////

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// transfernodes()    Transfer nodes from 'io->pointlist' to 'this->points'. //
//                                                                           //
// Initializing 'this->points'.  Transferring all points from 'in->pointlist'//
// into it. All points are indexed (start from in->firstnumber).  Each point //
// is initialized be UNUSEDVERTEX.  The bounding box (xmin, xmax, ymin, ymax,//
// zmin, zmax) and the diameter (longest) of the point set are calculated.   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::transfernodes()
{
  point pointloop;
  REAL x, y, z;
  int coordindex;
  int attribindex;
  int mtrindex;
  int i, j;

  // Read the points.
  coordindex = 0;
  attribindex = 0;
  mtrindex = 0;
  for (i = 0; i < in->numberofpoints; i++) {
    makepoint(&pointloop);
    // Read the point coordinates.
    x = pointloop[0] = in->pointlist[coordindex++];
    y = pointloop[1] = in->pointlist[coordindex++];
    z = pointloop[2] = in->pointlist[coordindex++];
    // Read the point attributes.
    for (j = 0; j < in->numberofpointattributes; j++) {
      pointloop[3 + j] = in->pointattributelist[attribindex++];
    }
    // Read the point metric tensor.
    for (j = 0; j < in->numberofpointmtrs; j++) {
      pointloop[pointmtrindex + j] = in->pointmtrlist[mtrindex++];
    }
    // Determine the smallest and largests x, y and z coordinates.
    if (i == 0) {
      xmin = xmax = x;
      ymin = ymax = y;
      zmin = zmax = z;
    } else {
      xmin = (x < xmin) ? x : xmin;
      xmax = (x > xmax) ? x : xmax;
      ymin = (y < ymin) ? y : ymin;
      ymax = (y > ymax) ? y : ymax;
      zmin = (z < zmin) ? z : zmin;
      zmax = (z > zmax) ? z : zmax;
    }
  }
  // 'longest' is the largest possible edge length formed by input vertices.
  x = xmax - xmin;
  y = ymax - ymin;
  z = zmax - zmin;
  longest = sqrt(x * x + y * y + z * z);
  if (longest == 0.0) {
   /* //printf("Error:  The point set is trivial.\n");
    terminatetetgen(3);*/
	  return;
  }
  // Two identical points are distinguished by 'lengthlimit'.
  lengthlimit = longest * b->epsilon * 1e+2;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// reconstructmesh()    Reconstruct a tetrahedral mesh.                      //
//                                                                           //
// The list of tetrahedra will be read from 'in->tetrahedronlist'. If 'in->  //
// trifacelist' is not empty, boundary faces (faces with a non-zero marker)  //
// from this list will be inserted into the mesh. In addition, this routine  //
// automatically detects boundary faces (subfaces): all hull faces will be   //
// recognized as subfaces, internal faces between two tetrahedra which have  //
// different region attributes will also be recognized as subfaces.          //
//                                                                           //
// Subsegments will be identified after subfaces are reconstructed. Edges at //
// the intersections of non-coplanar subfaces are recognized as subsegments. //
// Edges between two coplanar subfaces with different boundary markers are   //
// also recognized as subsegments.                                           //
//                                                                           //
// The facet index of each subface will be set automatically after we have   //
// recovered subfaces and subsegments.  That is, the set of subfaces, which  //
// are coplanar and have the same boundary marker will be recognized as a    //
// facet and has a unique index, stored as the facet marker in each subface  //
// of the set, the real boundary marker of each subface will be found in     //
// 'in->facetmarkerlist' by the index.  Facet index will be used in Delaunay //
// refinement for detecting two incident facets.                             //
//                                                                           //
// Points which are not corners of tetrahedra will be inserted into the mesh.//
// Return the number of faces on the hull after the reconstruction.          //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

long tetgenmesh::reconstructmesh()
{
  tetrahedron **tetsperverlist;
  shellface **facesperverlist;
  triface tetloop, neightet, neineightet, spintet;
  face subloop, neighsh, neineighsh;
  face sface1, sface2;
  face checkseg, subseg;
  point *idx2verlist;
  point torg, tdest, tapex, toppo;
  point norg, napex;
  list *neighshlist, *markerlist;
  REAL sign, attrib, volume;
  REAL da1, da2;
  bool bondflag, insertsegflag;
  int *idx2tetlist;
  int *idx2facelist;
  int *worklist;
  int facetidx, marker;
  int iorg, idest, iapex, ioppo;
  int pivot, ipivot, isum;
  int maxbandwidth;
  int index, i, j, k;

  if (!b->quiet) {
    //printf("Reconstructing mesh.\n");
  }

  // Create a map from index to points.
  makeindex2pointmap(idx2verlist);

  // Create the tetrahedra.
  for (i = 0; i < in->numberoftetrahedra; i++) {
    // Create a new tetrahedron and set its four corners, make sure that
    //   four corners form a positive orientation.
    maketetrahedron(&tetloop);
    index = i * in->numberofcorners;
    // Although there may be 10 nodes, we only read the first 4.
    iorg = in->tetrahedronlist[index] - in->firstnumber;
    idest = in->tetrahedronlist[index + 1] - in->firstnumber;
    iapex = in->tetrahedronlist[index + 2] - in->firstnumber;
    ioppo = in->tetrahedronlist[index + 3] - in->firstnumber;
    torg = idx2verlist[iorg];
    tdest = idx2verlist[idest];
    tapex = idx2verlist[iapex];
    toppo = idx2verlist[ioppo];
    sign = orient3d(torg, tdest, tapex, toppo);
    if (sign > 0.0) {
      norg = torg; torg = tdest; tdest = norg;
    } else if (sign == 0.0) {
      if (!b->quiet) {
        //printf("Warning:  Tet %d is degenerate.\n", i + in->firstnumber);
      }
    }
    setorg(tetloop, torg);
    setdest(tetloop, tdest);
    setapex(tetloop, tapex);
    setoppo(tetloop, toppo);
    // Temporarily set the vertices be type FREEVOLVERTEX, to indicate that
    //   they belong to the mesh.  These types may be changed later.
    setpointtype(torg, FREEVOLVERTEX);
    setpointtype(tdest, FREEVOLVERTEX);
    setpointtype(tapex, FREEVOLVERTEX);
    setpointtype(toppo, FREEVOLVERTEX);
    // Set element attributes if they exist.
    for (j = 0; j < in->numberoftetrahedronattributes; j++) {
      index = i * in->numberoftetrahedronattributes;
      attrib = in->tetrahedronattributelist[index + j];
      setelemattribute(tetloop.tet, j, attrib);
    }
    // If -a switch is used (with no number follows) Set a volume
    //   constraint if it exists.
    if (b->varvolume) {
      if (in->tetrahedronvolumelist != (REAL *) NULL) {
        volume = in->tetrahedronvolumelist[i];
      } else {
        volume = -1.0;
      }
      setvolumebound(tetloop.tet, volume);
    }
  }

  // Set the connection between tetrahedra.
  hullsize = 0l;
  // Create a map from nodes to tetrahedra.
  maketetrahedronmap(idx2tetlist, tetsperverlist);
  // Initialize the worklist.
  worklist = new int[points->items];
  for (i = 0; i < points->items; i++) worklist[i] = 0;
  maxbandwidth = 0;

  // Loop all tetrahedra, bond two tetrahedra if they share a common face.
  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  while (tetloop.tet != (tetrahedron *) NULL) {
    // Loop the four sides of the tetrahedron.
    for (tetloop.loc = 0; tetloop.loc < 4; tetloop.loc++) {
      sym(tetloop, neightet);
      if (neightet.tet != dummytet) continue; // This side has finished.
      torg = org(tetloop);
      tdest = dest(tetloop);
      tapex = apex(tetloop);
      iorg = pointmark(torg) - in->firstnumber;
      idest = pointmark(tdest) - in->firstnumber;
      iapex = pointmark(tapex) - in->firstnumber;
      worklist[iorg] = 1;
      worklist[idest] = 1;
      worklist[iapex] = 1;
      // Pick the vertex which has the lowest degree.
      if ((idx2tetlist[iorg + 1] - idx2tetlist[iorg]) > 
          (idx2tetlist[idest + 1] - idx2tetlist[idest])) {
        if ((idx2tetlist[idest + 1] - idx2tetlist[idest]) > 
            (idx2tetlist[iapex + 1] - idx2tetlist[iapex])) {
          pivot = iapex;
        } else {
          pivot = idest;
        }
      } else {
        if ((idx2tetlist[iorg + 1] - idx2tetlist[iorg]) > 
            (idx2tetlist[iapex + 1] - idx2tetlist[iapex])) {
          pivot = iapex;
        } else {
          pivot = iorg;
        }
      }
      if ((idx2tetlist[pivot + 1] - idx2tetlist[pivot]) > maxbandwidth) {
        maxbandwidth = idx2tetlist[pivot + 1] - idx2tetlist[pivot];
      }
      bondflag = false;
      // Search its neighbor in the adjacent tets of the pivoted vertex.
      for (j = idx2tetlist[pivot]; j < idx2tetlist[pivot + 1] && !bondflag; 
           j++) {
        // Quickly check if this tet contains the neighbor.
        isum = 0;
        for (k = 0; k < 4; k++) {
          norg = (point) tetsperverlist[j][4 + k];
          ipivot = pointmark(norg) - in->firstnumber;
          isum += worklist[ipivot];
        }
        if (isum != 3) continue;
        if (tetsperverlist[j] == tetloop.tet) continue; // Skip myself.
        // This tet contains its neighbor, find the face and bond them.
        neightet.tet = tetsperverlist[j];
        for (neightet.loc = 0; neightet.loc < 4; neightet.loc++) {
          norg = oppo(neightet);
          ipivot = pointmark(norg) - in->firstnumber;
          if (worklist[ipivot] == 0) {
            // Find! Bond them together and break the loop.
#ifdef SELF_CHECK
            sym(neightet, neineightet);
            //assert(neineightet.tet == dummytet);
#endif
            bond(tetloop, neightet);
            bondflag = true;
            break;
          }
        }
      }
      if (!bondflag) {
        hullsize++;  // It's a hull face.
        // Bond this side to outer space.
        dummytet[0] = encode(tetloop);
        if ((in->pointmarkerlist != (int *) NULL) && !b->coarse) {
          // Set its three corners's markers be boundary (hull) vertices.
          if (in->pointmarkerlist[iorg] == 0) {
            in->pointmarkerlist[iorg] = 1;
          }
          if (in->pointmarkerlist[idest] == 0) {
            in->pointmarkerlist[idest] = 1;
          }
          if (in->pointmarkerlist[iapex] == 0) {
            in->pointmarkerlist[iapex] = 1;
          }
        }
      }
      worklist[iorg] = 0;
      worklist[idest] = 0;
      worklist[iapex] = 0;
    }
    tetloop.tet = tetrahedrontraverse();
  }

  if (b->verbose) {
    //printf("  Maximal vertex degree = %d.\n", maxbandwidth);
  }

  // Subfaces will be inserted into the mesh. It has two phases:
  //   (1) Insert subfaces provided by user (in->trifacelist);
  //   (2) Create subfaces for hull faces (if they're not subface yet) and
  //       interior faces which separate two different materials.

  // Phase (1). Is there a list of user-provided subfaces?
  if (in->trifacelist != (int *) NULL) {
    // Recover subfaces from 'in->trifacelist'.
    for (i = 0; i < in->numberoftrifaces; i++) {
      index = i * 3;
      iorg = in->trifacelist[index] - in->firstnumber;
      idest = in->trifacelist[index + 1] - in->firstnumber;
      iapex = in->trifacelist[index + 2] - in->firstnumber;
      // Look for the location of this subface.
      worklist[iorg] = 1;
      worklist[idest] = 1;
      worklist[iapex] = 1;
      // Pick the vertex which has the lowest degree.
      if ((idx2tetlist[iorg + 1] - idx2tetlist[iorg]) > 
          (idx2tetlist[idest + 1] - idx2tetlist[idest])) {
        if ((idx2tetlist[idest + 1] - idx2tetlist[idest]) > 
            (idx2tetlist[iapex + 1] - idx2tetlist[iapex])) {
          pivot = iapex;
        } else {
          pivot = idest;
        }
      } else {
        if ((idx2tetlist[iorg + 1] - idx2tetlist[iorg]) > 
            (idx2tetlist[iapex + 1] - idx2tetlist[iapex])) {
          pivot = iapex;
        } else {
          pivot = iorg;
        }
      }
      bondflag = false;
      // Search its neighbor in the adjacent tets of torg.
      for (j = idx2tetlist[pivot]; j < idx2tetlist[pivot + 1] && !bondflag; 
           j++) {
        // Quickly check if this tet contains the neighbor.
        isum = 0;
        for (k = 0; k < 4; k++) {
          norg = (point) tetsperverlist[j][4 + k];
          ipivot = pointmark(norg) - in->firstnumber;
          isum += worklist[ipivot];
        }
        if (isum != 3) continue;
        neightet.tet = tetsperverlist[j];
        for (neightet.loc = 0; neightet.loc < 4; neightet.loc++) {
          norg = oppo(neightet);
          ipivot = pointmark(norg) - in->firstnumber;
          if (worklist[ipivot] == 0) {
            bondflag = true;  // Find!
            break;
          }
        }
      }
      if (bondflag) {
        // Create a new subface and insert it into the mesh.
        makeshellface(subfaces, &subloop);
        torg = idx2verlist[iorg];
        tdest = idx2verlist[idest];
        tapex = idx2verlist[iapex];
        setsorg(subloop, torg);
        setsdest(subloop, tdest);
        setsapex(subloop, tapex);
        // Set the vertices be FREESUBVERTEX to indicate they belong to a
        //   facet of the domain.  They may be changed later.
        setpointtype(torg, FREESUBVERTEX);
        setpointtype(tdest, FREESUBVERTEX);
        setpointtype(tapex, FREESUBVERTEX);
        if (in->trifacemarkerlist != (int *) NULL) {
          setshellmark(subloop, in->trifacemarkerlist[i]);
        }
        adjustedgering(neightet, CCW);
        findedge(&subloop, org(neightet), dest(neightet));
        tsbond(neightet, subloop);
        sym(neightet, neineightet);
        if (neineightet.tet != dummytet) {
          sesymself(subloop);
          tsbond(neineightet, subloop);
        }
      } else {
        if (!b->quiet) {
          //printf("Warning:  Subface %d is discarded.\n", i + in->firstnumber);
        }
      }
      worklist[iorg] = 0;
      worklist[idest] = 0;
      worklist[iapex] = 0;
    }
  } 

  // Phase (2). Indentify subfaces from the mesh.
  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  while (tetloop.tet != (tetrahedron *) NULL) {
    // Loop the four sides of the tetrahedron.
    for (tetloop.loc = 0; tetloop.loc < 4; tetloop.loc++) {
      tspivot(tetloop, subloop);
      if (subloop.sh != dummysh) continue;
      bondflag = false;
      sym(tetloop, neightet);
      if (neightet.tet == dummytet) {
        // It's a hull face. Insert a subface at here.
        bondflag = true;
      } else {
        // It's an interior face. Insert a subface if two tetrahedra have
        //   different attributes (i.e., they belong to two regions).
        if (in->numberoftetrahedronattributes > 0) {
          if (elemattribute(neightet.tet,
              in->numberoftetrahedronattributes - 1) != 
              elemattribute(tetloop.tet,
              in->numberoftetrahedronattributes - 1)) {
            bondflag = true;
          }
        }
      }
      if (bondflag) {
        adjustedgering(tetloop, CCW);
        makeshellface(subfaces, &subloop);
        torg = org(tetloop);
        tdest = dest(tetloop);
        tapex = apex(tetloop);
        setsorg(subloop, torg);
        setsdest(subloop, tdest);
        setsapex(subloop, tapex);
        // Set the vertices be FREESUBVERTEX to indicate they belong to a
        //   facet of the domain.  They may be changed later.
        setpointtype(torg, FREESUBVERTEX);
        setpointtype(tdest, FREESUBVERTEX);
        setpointtype(tapex, FREESUBVERTEX);
        tsbond(tetloop, subloop);
        if (neightet.tet != dummytet) {
          sesymself(subloop);
          tsbond(neightet, subloop);
        }
      }
    }
    tetloop.tet = tetrahedrontraverse();
  }

  // Set the connection between subfaces. A subsegment may have more than
  //   two subfaces sharing it, 'neighshlist' stores all subfaces sharing
  //   one edge.
  neighshlist = new list(sizeof(face), NULL);
  // Create a map from nodes to subfaces.
  makesubfacemap(idx2facelist, facesperverlist);

  // Loop over the set of subfaces, setup the connection between subfaces.
  subfaces->traversalinit();
  subloop.sh = shellfacetraverse(subfaces);
  while (subloop.sh != (shellface *) NULL) {
    for (i = 0; i < 3; i++) {
      spivot(subloop, neighsh);
      if (neighsh.sh == dummysh) {
        // This side is 'empty', operate on it.
        torg = sorg(subloop);
        tdest = sdest(subloop);
        tapex = sapex(subloop);
        neighshlist->append(&subloop);
        iorg = pointmark(torg) - in->firstnumber;
        // Search its neighbor in the adjacent list of torg.
        for (j = idx2facelist[iorg]; j < idx2facelist[iorg + 1]; j++) {
          neighsh.sh = facesperverlist[j];
          if (neighsh.sh == subloop.sh) continue;
          neighsh.shver = 0;
          if (isfacehasedge(&neighsh, torg, tdest)) {
            findedge(&neighsh, torg, tdest);
            // Insert 'neighsh' into 'neighshlist'.
            if (neighshlist->len() < 2) {
              neighshlist->append(&neighsh);
            } else {
              for (index = 0; index < neighshlist->len() - 1; index++) {
                sface1 = * (face *)(* neighshlist)[index];
                sface2 = * (face *)(* neighshlist)[index + 1];
                da1 = facedihedral(torg, tdest, sapex(sface1), sapex(neighsh));
                da2 = facedihedral(torg, tdest, sapex(sface1), sapex(sface2));
                if (da1 < da2) {
                  break;  // Insert it after index.
                }
              }
              neighshlist->insert(index + 1, &neighsh);
            }
          }
        }
        // Bond the subfaces in 'neighshlist'. 
        if (neighshlist->len() > 1) {
          neighsh = * (face *)(* neighshlist)[0];
          for (j = 1; j <= neighshlist->len(); j++) {
            if (j < neighshlist->len()) {
              neineighsh = * (face *)(* neighshlist)[j];
            } else {
              neineighsh = * (face *)(* neighshlist)[0];
            }
            sbond1(neighsh, neineighsh);
            neighsh = neineighsh;
          }
        } else {
          // No neighbor subface be found, bond 'subloop' to itself.
          sdissolve(subloop); // sbond(subloop, subloop);
        }
        neighshlist->clear();
      }
      senextself(subloop);
    }
    subloop.sh = shellfacetraverse(subfaces);
  }

  
  // Segments will be introudced. Each segment has a unique marker (1-based).
  marker = 1;
  subfaces->traversalinit();
  subloop.sh = shellfacetraverse(subfaces);
  while (subloop.sh != (shellface *) NULL) {
    for (i = 0; i < 3; i++) {
      sspivot(subloop, subseg);
      if (subseg.sh == dummysh) {
        // This side has no subsegment bonded, check it.
        torg = sorg(subloop);
        tdest = sdest(subloop);
        tapex = sapex(subloop);
        spivot(subloop, neighsh);
        spivot(neighsh, neineighsh);
        insertsegflag = false;
        if (subloop.sh == neighsh.sh || subloop.sh != neineighsh.sh) {
          // This side is either self-bonded or more than two subfaces,
          //   insert a subsegment at this side.
          insertsegflag = true;
        } else {
          // Only two subfaces case.
#ifdef SELF_CHECK
          //assert(subloop.sh != neighsh.sh);
#endif
          napex = sapex(neighsh);
          sign = orient3d(torg, tdest, tapex, napex);
          if (iscoplanar(torg, tdest, tapex, napex, sign, b->epsilon)) {
            // Although they are coplanar, we still need to check if they
            //   have the same boundary marker.
            insertsegflag = (shellmark(subloop) != shellmark(neighsh));
          } else {
            // Non-coplanar.
            insertsegflag = true;
          }
        }
        if (insertsegflag) {
          // Create a subsegment at this side.
          makeshellface(subsegs, &subseg);
          setsorg(subseg, torg);
          setsdest(subseg, tdest);
          // The two vertices have been marked as FREESUBVERTEX. Now mark
          //   them as NACUTEVERTEX.
          setpointtype(torg, NACUTEVERTEX);
          setpointtype(tdest, NACUTEVERTEX);
          setshellmark(subseg, marker);
          marker++;
          // Bond all subfaces to this subsegment.
          neighsh = subloop;
          do {
            ssbond(neighsh, subseg);
            spivotself(neighsh);
            if (neighsh.sh == dummysh) {
              break; // Only one facet case.
            }
          } while (neighsh.sh != subloop.sh);
        }
      }
      senextself(subloop);
    }
    subloop.sh = shellfacetraverse(subfaces);
  }

  // Remember the number of input segments.
  insegments = subsegs->items;
  // Find the acute vertices and set them be type ACUTEVERTEX.

  // Indentify facets and set the facet marker (1-based) for subfaces.
  markerlist = new list(sizeof(int), NULL, 256);
  
  subfaces->traversalinit();
  subloop.sh = shellfacetraverse(subfaces);
  while (subloop.sh != (shellface *) NULL) {
    // Only operate on uninfected subface, after operating, infect it.
    if (!sinfected(subloop)) {
      // A new facet is found.
      marker = shellmark(subloop);
      markerlist->append(&marker);
      facetidx = markerlist->len(); // 'facetidx' starts from 1.
      setshellmark(subloop, facetidx);
      sinfect(subloop);
      neighshlist->append(&subloop);
      // Find out all subfaces of this facet (bounded by subsegments).
      for (i = 0; i < neighshlist->len(); i++) {
        neighsh = * (face *) (* neighshlist)[i];
        for (j = 0; j < 3; j++) {
          sspivot(neighsh, subseg);
          if (subseg.sh == dummysh) {
            spivot(neighsh, neineighsh);
            if (!sinfected(neineighsh)) {
              // 'neineighsh' is in the same facet as 'subloop'.
#ifdef SELF_CHECK
              //assert(shellmark(neineighsh) == marker);
#endif
              setshellmark(neineighsh, facetidx);
              sinfect(neineighsh);
              neighshlist->append(&neineighsh);
            }
          }
          senextself(neighsh);
        }
      }
      neighshlist->clear();
    }
    subloop.sh = shellfacetraverse(subfaces);
  }
  // Uninfect all subfaces.
  subfaces->traversalinit();
  subloop.sh = shellfacetraverse(subfaces);
  while (subloop.sh != (shellface *) NULL) {
#ifdef SELF_CHECK
    //assert(sinfected(subloop));
#endif
    suninfect(subloop);
    subloop.sh = shellfacetraverse(subfaces);
  }
  // Save the facet markers in 'in->facetmarkerlist'.
  in->numberoffacets = markerlist->len();
  in->facetmarkerlist = new int[in->numberoffacets];
  for (i = 0; i < in->numberoffacets; i++) {
    marker = * (int *) (* markerlist)[i];
    in->facetmarkerlist[i] = marker;
  }
  // Initialize the 'facetabovepointlist'.
  facetabovepointarray = new point[in->numberoffacets + 1];
  for (i = 0; i < in->numberoffacets + 1; i++) {
    facetabovepointarray[i] = (point) NULL;
  }

  // The mesh contains boundary now.
  checksubfaces = 1;
  // The mesh is nonconvex now.
  nonconvex = 1;

  /*// Is there periodic boundary confitions?
  if (checkpbcs) {
    tetgenio::pbcgroup *pg;
    pbcdata *pd;
    // Initialize the global array 'subpbcgrouptable'.
    createsubpbcgrouptable();
    // Loop for each pbcgroup i.
    for (i = 0; i < in->numberofpbcgroups; i++) {
      pg = &(in->pbcgrouplist[i]);
      pd = &(subpbcgrouptable[i]);
      // Find all subfaces of pd, set each subface's group id be i.
      for (j = 0; j < 2; j++) {
        subfaces->traversalinit();
        subloop.sh = shellfacetraverse(subfaces);
        while (subloop.sh != (shellface *) NULL) {
          facetidx = shellmark(subloop);
          marker = in->facetmarkerlist[facetidx - 1];
          if (marker == pd->fmark[j]) {
            setshellpbcgroup(subloop, i);
            pd->ss[j] = subloop;
          }
          subloop.sh = shellfacetraverse(subfaces);
        }
      }
      if (pg->pointpairlist != (int *) NULL) {
        // Set the connections between pbc point pairs.
        for (j = 0; j < pg->numberofpointpairs; j++) {
          iorg = pg->pointpairlist[j * 2] - in->firstnumber;
          idest = pg->pointpairlist[j * 2 + 1] - in->firstnumber;
          torg = idx2verlist[iorg];
          tdest = idx2verlist[idest];
          setpoint2pbcpt(torg, tdest);
          setpoint2pbcpt(tdest, torg);
        }
      }
    }
    // Create the global array 'segpbcgrouptable'.
    createsegpbcgrouptable();
  }*/

  delete markerlist;
  delete neighshlist;
  delete [] worklist;
  delete [] idx2tetlist;
  delete [] tetsperverlist;
  delete [] idx2facelist;
  delete [] facesperverlist;
  delete [] idx2verlist;
  
  return hullsize;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// insertconstrainedpoints()    Insert a list of constrained points.         //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::insertconstrainedpoints(tetgenio *addio)
{
  queue *flipqueue;
  triface searchtet;
  face checksh, checkseg;
  point newpoint;
  enum locateresult loc;
  REAL *attr;
  bool insertflag;
  int covertices, outvertices;
  int index;
  int i, j;
  
  if (!b->quiet) {
    //printf("Insert additional points into mesh.\n");
  }

  // Initialize 'flipqueue'.
  flipqueue = new queue(sizeof(badface));
  recenttet.tet = dummytet;
  covertices = outvertices = 0;

  index = 0;
  for (i = 0; i < addio->numberofpoints; i++) {
    // Create a newpoint.
    makepoint(&newpoint);
    newpoint[0] = addio->pointlist[index++];
    newpoint[1] = addio->pointlist[index++];
    newpoint[2] = addio->pointlist[index++];
    // Read the add point attributes if current points have attributes.
    if ((addio->numberofpointattributes > 0) &&
        (in->numberofpointattributes > 0)) {
      attr = addio->pointattributelist + addio->numberofpointattributes * i;
      for (j = 0; j < in->numberofpointattributes; j++) {
        if (j < addio->numberofpointattributes) {
          newpoint[3 + j] = attr[j];
        }
      }
    }
    // Find the location of the inserted point.
    searchtet = recenttet;
    loc = locate(newpoint, &searchtet);
    if (loc != ONVERTEX) {
      loc = adjustlocate(newpoint, &searchtet, loc, b->epsilon2);
    }
    if (loc == OUTSIDE) {
      loc = hullwalk(newpoint, &searchtet);
      if (loc == OUTSIDE) {
        // Perform a brute-force search.
        tetrahedrons->traversalinit();
        searchtet.tet = tetrahedrontraverse();
        while (searchtet.tet != (tetrahedron *) NULL) {
          loc = adjustlocate(newpoint, &searchtet, OUTSIDE, b->epsilon2);
          if (loc != OUTSIDE) break;
          searchtet.tet = tetrahedrontraverse();
        }
      }
    }
    // Insert the point if it not lies outside or on a vertex.
    insertflag = true;
    switch (loc) {
    case INTETRAHEDRON:
      setpointtype(newpoint, FREEVOLVERTEX);
      splittetrahedron(newpoint, &searchtet, flipqueue);
      break;
    case ONFACE:
      tspivot(searchtet, checksh);
      if (checksh.sh != dummysh) {
        // It is a boundary face. Don't insert it if -Y option is used.
        if (b->nobisect) {
          insertflag = false;
        } else {
          setpointtype(newpoint, FREESUBVERTEX);
          setpoint2sh(newpoint, sencode(checksh));
        }
      } else {
        setpointtype(newpoint, FREEVOLVERTEX);
      }
      if (insertflag) {
        splittetface(newpoint, &searchtet, flipqueue);
      }
      break;
    case ENCSEGMENT:
    case ONEDGE:
      tsspivot(&searchtet, &checkseg);
      if (checkseg.sh != dummysh) {
        if (b->nobisect) {
          insertflag = false;
        } else {
          setpointtype(newpoint, FREESEGVERTEX);
          setpoint2seg(newpoint, sencode(checkseg));
        }
      } else {
        tspivot(searchtet, checksh);
        if (checksh.sh != dummysh) {
          if (b->nobisect) {
            insertflag = false;
          } else {
            setpointtype(newpoint, FREESUBVERTEX);
            setpoint2sh(newpoint, sencode(checksh));
          }
        } else {
          setpointtype(newpoint, FREEVOLVERTEX);
        }
      }
      if (insertflag) {
        splittetedge(newpoint, &searchtet, flipqueue);
      }
      break;
    case ONVERTEX:
      insertflag = false;
      covertices++;
      break;
    case OUTSIDE:
      insertflag = false;
      outvertices++;
      break;
    }
    // Remember the tetrahedron for next point searching.
    recenttet = searchtet;
    if (!insertflag) {
      pointdealloc(newpoint);
    } else {
      lawson3d(flipqueue);
    }
  }

  if (b->verbose) {
    if (covertices > 0) {
      //printf("  %d constrained points already exist.\n", covertices);
    }
    if (outvertices > 0) {
      //printf("  %d constrained points lie outside the mesh.\n", outvertices);
    }
    //printf("  %d constrained points have been inserted.\n", 
           //addio->numberofpoints - covertices - outvertices);
  }

  delete flipqueue;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// p1interpolatebgm()    Set pt size by p^1 interpolation in background mesh.//
//                                                                           //
// On input, 'bgmtet' is a suggesting tet in background mesh for searching   //
// 'pt'. It returns the tet containing 'pt'.                                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::p1interpolatebgm(point pt, triface* bgmtet, long *scount)
{
  point bgmpt[4];
  enum locateresult loc;
  REAL vol, volpt[4], weights[4];
  int i;

  loc = bgm->preciselocate(pt, bgmtet, bgm->tetrahedrons->items);
  if (loc == OUTSIDE) {
    loc = bgm->hullwalk(pt, bgmtet);
    if (loc == OUTSIDE) {
      // Perform a brute-force search.
      if (!b->quiet && b->verbose) {
        //printf("Warning:  Global point location.\n");
      }
      if (scount) (*scount)++;
      bgm->tetrahedrons->traversalinit(); // in bgm
      bgmtet->tet = bgm->tetrahedrontraverse(); // in bgm
      while (bgmtet->tet != (tetrahedron *) NULL) {
        loc = bgm->adjustlocate(pt, bgmtet, OUTSIDE, b->epsilon);
        if (loc != OUTSIDE) break;
        bgmtet->tet = bgm->tetrahedrontraverse(); // in bgm
      }
    }
  }
  if (loc != OUTSIDE) {
    // Let p remember t.
    setpoint2bgmtet(pt, encode(*bgmtet)); // in m
    // get the corners of t.
    for (i = 0; i < 4; i++) bgmpt[i] = (point) bgmtet->tet[4 + i];
    // Calculate the weighted coordinates of p in t.
    vol = orient3d(bgmpt[0], bgmpt[1], bgmpt[2], bgmpt[3]);
    volpt[0] = orient3d(pt, bgmpt[1], bgmpt[2], bgmpt[3]);
    volpt[1] = orient3d(bgmpt[0], pt, bgmpt[2], bgmpt[3]);
    volpt[2] = orient3d(bgmpt[0], bgmpt[1], pt, bgmpt[3]);
    volpt[3] = orient3d(bgmpt[0], bgmpt[1], bgmpt[2], pt);
    for (i = 0; i < 4; i++) weights[i] = fabs(volpt[i] / vol);
    // Interpolate the solution for p.
    for (i = 0; i < bgm->in->numberofpointmtrs; i++) {
      pt[pointmtrindex + i] = weights[0] * bgmpt[0][bgm->pointmtrindex + i]
                            + weights[1] * bgmpt[1][bgm->pointmtrindex + i]
                            + weights[2] * bgmpt[2][bgm->pointmtrindex + i]
                            + weights[3] * bgmpt[3][bgm->pointmtrindex + i];
    }
  } else {
    setpoint2bgmtet(pt, (tetrahedron) NULL);  // in m
  }
  return loc != OUTSIDE;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// interpolatesizemap()    Interpolate the point sizes in the given size map.//
//                                                                           //
// The size map is specified on each node of the background mesh. The points //
// of current mesh get their sizes by interpolating.                         //
//                                                                           //
// This function operation on two meshes simultaneously, the current mesh m, //
// and the background mesh bgm. After this function, each point p in m will  //
// have a pointer to a tet of bgm.                                           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::interpolatesizemap()
{
  list *adjtetlist;
  triface tetloop, neightet, bgmtet;
  point searchpt;
  long scount;
  int *worklist;
  int sepcount;
  int i;

  if (b->verbose) {
    //printf("  Interpolating size map.\n");
  }

  worklist = new int[points->items + 1];
  for (i = 0; i < points->items + 1; i++) worklist[i] = 0;
  sepcount = 0;
  scount = 0l;

  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  while (tetloop.tet != (tetrahedron *) NULL) {
    if (!infected(tetloop)) {
      // Find a new subdomain.
      adjtetlist = new list(sizeof(triface), NULL, 1024);
      infect(tetloop);
      // Search the four corners in background mesh.
      for (i = 0; i < 4; i++) {
        searchpt = (point) tetloop.tet[4 + i];
        // Mark the point for avoiding multiple searchings.
        // //assert(worklist[pointmark(searchpt)] == 0);
        worklist[pointmark(searchpt)] = 1;
        // Does it contain a pointer to bgm tet?
        bgm->decode(point2bgmtet(searchpt), bgmtet);
        if (bgm->isdead(&bgmtet)) {
          bgmtet = bgm->recenttet;
        }
        if (p1interpolatebgm(searchpt, &bgmtet, &scount)) {
          bgm->recenttet = bgmtet;
        }
      } // for (i = 0; i < 4; i++)
      // Collect all tets in this region.
      adjtetlist->append(&tetloop);
      // Collect the tets in the subdomain.
      for (i = 0; i < adjtetlist->len(); i++) {
        tetloop = * (triface *)(* adjtetlist)[i];
        for (tetloop.loc = 0; tetloop.loc < 4; tetloop.loc++) {
          sym(tetloop, neightet);
          if ((neightet.tet != dummytet) && !infected(neightet)) {
            // Only need to search for the opposite point.
            searchpt = oppo(neightet);
            if (worklist[pointmark(searchpt)] == 0) {
              worklist[pointmark(searchpt)] = 1;
              decode(point2bgmtet(searchpt), bgmtet);
              if (bgm->isdead(&bgmtet)) {
                bgmtet = bgm->recenttet;
              }
              if (p1interpolatebgm(searchpt, &bgmtet, &scount)) {
                bgm->recenttet = bgmtet;
              }
            }
            infect(neightet);
            adjtetlist->append(&neightet);
          }
        }
      }
      // Increase the number of separated domains.
      sepcount++;
      delete adjtetlist;
    } // if (!infect())
    tetloop.tet = tetrahedrontraverse();
  }

  // Unmark all tets.
  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  while (tetloop.tet != (tetrahedron *) NULL) {
    //assert(infected(tetloop));
    uninfect(tetloop);
    tetloop.tet = tetrahedrontraverse();
  }
  delete [] worklist;

#ifdef SELF_CHECK
  if (b->verbose && scount > 0l) {
    //printf("  %ld brute-force searches.\n", scount);
  }
  if (b->verbose && sepcount > 0) {
    //printf("  %d separate domains.\n", sepcount);
  }
#endif
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// duplicatebgmesh()    Duplicate current mesh to background mesh.           //
//                                                                           //
// Current mesh 'this' is copied into 'this->bgm'.Both meshes share the same //
// input tetgenio object, 'this->in', same tetgenbehavior object 'this->b'.  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::duplicatebgmesh()
{
  triface tetloop, btetloop;
  triface symtet, bsymtet;
  face bhullsh, bneighsh;
  point *idx2bplist, *tetptbaklist;
  point ploop, bploop;
  int idx, i;

  if (!b->quiet) {
    //printf("Duplicating background mesh.\n");
  }

  // The background mesh itself has no background mesh.
  // //assert(bgm->bgm == (tetgenmesh *) NULL);
  // The space for metric tensor should be allocated.
  // //assert(bgm->sizeoftensor > 0);

  // Copy point list.
  idx2bplist = new point[points->items + 1];
  idx = in->firstnumber;
  points->traversalinit();
  ploop = pointtraverse();
  while (ploop != (point) NULL) {
    bgm->makepoint(&bploop);
    // Copy coordinates, attributes.
    for (i = 0; i < 3 + in->numberofpointattributes; i++) {
      bploop[i] = ploop[i];
    }
    // Transfer the metric tensor.
    for (i = 0; i < bgm->sizeoftensor; i++) {
      bploop[bgm->pointmtrindex + i] = ploop[pointmtrindex + i];
      // Metric tensor should have a positive value.
      if (bploop[bgm->pointmtrindex + i] <= 0.0) {
        //printf("Error:  Point %d has non-positive size %g (-m option).\n",
            //   bgm->pointmark(bploop), bploop[bgm->pointmtrindex + i]);
        terminatetetgen(3);
      }
    }
    // Remember the point for searching.
    idx2bplist[idx++] = bploop; 
    ploop = pointtraverse();
  }

  // Copy tetrahedra list.
  tetptbaklist = new point[tetrahedrons->items + 1];
  idx = in->firstnumber;
  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  while (tetloop.tet != (tetrahedron *) NULL) {
    bgm->maketetrahedron(&btetloop);
    // Set the four corners.
    for (i = 0; i < 4; i++) {
      ploop = (point) tetloop.tet[4 + i];
      bploop = idx2bplist[pointmark(ploop)];
      btetloop.tet[4 + i] = (tetrahedron) bploop;
    }
    // Remember the tet for setting neighbor connections.
    tetptbaklist[idx++] = (point) tetloop.tet[4];
    tetloop.tet[4] = (tetrahedron) btetloop.tet; 
    tetloop.tet = tetrahedrontraverse();
  }

  // Set the connections between background tetrahedra. Create background
  //   hull subfaces. Create the map of point-to-bgmtet. 
  idx = in->firstnumber;
  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  while (tetloop.tet != (tetrahedron *) NULL) {
    // Get the corresponding background tet.
    btetloop.tet = (tetrahedron *) tetloop.tet[4];
    // Set the four neighbors.
    for (tetloop.loc = 0; tetloop.loc < 4; tetloop.loc++) {
      btetloop.loc = tetloop.loc;
      sym(tetloop, symtet);
      if ((symtet.tet != dummytet) && (symtet.tet > tetloop.tet)) {
        // Operate on the un-connected interior face.
        bsymtet.tet = (tetrahedron *) symtet.tet[4]; // The saved bgm tet.
        bsymtet.loc = symtet.loc;
        bgm->bond(btetloop, bsymtet);
      } else if (symtet.tet == dummytet) {
        // Create a subface in background mesh.
        bgm->makeshellface(bgm->subfaces, &bhullsh);
        bgm->adjustedgering(btetloop, CCW); // face to inside.
        bgm->setsorg(bhullsh, bgm->org(btetloop));
        bgm->setsdest(bhullsh, bgm->dest(btetloop));
        bgm->setsapex(bhullsh, bgm->apex(btetloop));
        bgm->tsbond(btetloop, bhullsh);
        // Remember a hull face for point location.
        bgm->dummytet[0] = bgm->encode(btetloop);
      }
    }
    // Restore the backup tet point.
    tetloop.tet[4] = (tetrahedron) tetptbaklist[idx++];
    // Make the point-to-bgmtet map for size interpolation.
    btetloop.loc = 0;
    for (i = 0; i < 4; i++) {
      ploop = (point) tetloop.tet[4 + i];
      setpoint2bgmtet(ploop, bgm->encode(btetloop));
    } 
    // Go to the next tet, btet.
    tetloop.tet = tetrahedrontraverse();
  }

  // Connect bgm hull subfaces. Note: all hull subfaces form a 2-manifold.
  bgm->subfaces->traversalinit();
  bhullsh.sh = bgm->shellfacetraverse(bgm->subfaces);
  while (bhullsh.sh != (shellface *) NULL) {
    bhullsh.shver = 0;
    bgm->stpivot(bhullsh, btetloop);
    //assert(btetloop.tet != bgm->dummytet);
    bgm->adjustedgering(btetloop, CCW);
    for (i = 0; i < 3; i++) {
      bgm->spivot(bhullsh, bneighsh);
      if (bneighsh.sh == bgm->dummysh) {
        // This side is open, operate on it.
        bsymtet = btetloop;
        while (bgm->fnextself(bsymtet));
        bgm->tspivot(bsymtet, bneighsh);
        bgm->findedge(&bneighsh, bgm->sdest(bhullsh), bgm->sorg(bhullsh));
        bgm->sbond(bhullsh, bneighsh);
      }
      bgm->enextself(btetloop);
      bgm->senextself(bhullsh);
    }
    bhullsh.sh = bgm->shellfacetraverse(bgm->subfaces);
  }

  delete [] tetptbaklist;
  delete [] idx2bplist;
}

////                                                                       ////
////                                                                       ////
//// reconstruct_cxx //////////////////////////////////////////////////////////

//// refine_cxx ///////////////////////////////////////////////////////////////
////                                                                       ////
////                                                                       ////

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// marksharpsegments()    Mark sharp segments.                               //
//                                                                           //
// A segment s is called sharp if it is in one of the two cases:             //
//  (1) There is a segment s' intersecting with s.  The internal angle (*)   //
//      between s and s' is acute.                                           //
//  (2) There are two facets f1 and f2 intersecting at s.  The internal      //
//      dihedral angle (*) between f1 and f2 is acute.                       //
// This routine finds the sharp segments and marked them as type SHARP.      //
//                                                                           //
// The minimum angle between segments (minfaceang) and the minimum dihedral  //
// angle between facets (minfacetdihed) are calulcated.                      //
//                                                                           //
// (*) The internal angle (or dihedral) bewteen two features means the angle //
// inside the mesh domain.                                                   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::marksharpsegments(REAL sharpangle)
{
  triface adjtet;
  face startsh, spinsh, neighsh;
  face segloop, prevseg, nextseg;
  point eorg, edest;
  REAL ang, smallang;
  bool issharp;
  int sharpsegcount;

  if (b->verbose > 0) {
    //printf("  Marking sharp segments.\n");
  }

  smallang = sharpangle * PI / 180.;
  sharpsegcount = 0;
  eorg = edest = (point) NULL; // To avoid compiler warnings.
   
  // A segment s may have been split into many subsegments. Operate the one
  //   which contains the origin of s. Then mark the rest of subsegments.
  subsegs->traversalinit();
  segloop.sh = shellfacetraverse(subsegs);
  while (segloop.sh != (shellface *) NULL) {
    segloop.shver = 0;
    senext2(segloop, prevseg);
    spivotself(prevseg);
    if (prevseg.sh == dummysh) {
      // Operate on this seg s.
      issharp = false;
      spivot(segloop, startsh);
      if (startsh.sh != dummysh) {
        // First check if two facets form an acute dihedral angle at s.
        eorg = sorg(segloop);
        edest = sdest(segloop);
        spinsh = startsh;
        do {
          if (sorg(spinsh) != eorg) {
            sesymself(spinsh);
          }
          // Only do test when the spinsh is faceing inward.
          stpivot(spinsh, adjtet);          
          if (adjtet.tet != dummytet) {
            // Get the subface on the adjacent facet.
            spivot(spinsh, neighsh);
            // Do not calculate if it is self-bonded.
            if ((neighsh.sh != dummysh) && (neighsh.sh != spinsh.sh)) {
              // Calculate the dihedral angle between the two subfaces.
              ang = facedihedral(eorg, edest, sapex(spinsh), sapex(neighsh));
              // Only do check if a sharp angle has not been found.
              if (!issharp) issharp = (ang < smallang);
              // Remember the smallest facet dihedral angle.
              minfacetdihed = minfacetdihed < ang ? minfacetdihed : ang;
            }
          }
          // Go to the next facet.
          spivotself(spinsh);
          if (spinsh.sh == dummysh) break; // A single subface case.
        } while (spinsh.sh != startsh.sh);
        // if (!issharp) {
          // Second check if s forms an acute angle with another seg.
          spinsh = startsh;
          do {
            if (sorg(spinsh) != eorg) {
              sesymself(spinsh);
            }
            // Calculate the angle between s and s' of this facet.
            neighsh = spinsh;
            // Rotate edges around 'eorg' until meeting another seg s'. Such  
            //   seg (s') must exist since the facet is segment-bounded.
            //   The sum of the angles of faces at 'eorg' gives the internal
            //   angle between the two segments.
            ang = 0.0;
            do {
              ang += interiorangle(eorg, sdest(neighsh), sapex(neighsh), NULL);
              senext2self(neighsh);
              sspivot(neighsh, nextseg);
              if (nextseg.sh != dummysh) break;
              // Go to the next coplanar subface.
              spivotself(neighsh);
              //assert(neighsh.sh != dummysh);
              if (sorg(neighsh) != eorg) {
                sesymself(neighsh);
              }
            } while (true);
            // Only do check if a sharp angle has not been found.
            if (!issharp) issharp = (ang < smallang);
            // Remember the smallest input face angle.
            minfaceang = minfaceang < ang ? minfaceang : ang;
            // Go to the next facet.
            spivotself(spinsh);
            if (spinsh.sh == dummysh) break; // A single subface case.
          } while (spinsh.sh != startsh.sh);
        // }
      }
      if (issharp) {
        setshelltype(segloop, SHARP);
        // Set the type for all subsegments at forwards.
        edest = sdest(segloop);
        senext(segloop, nextseg);
        spivotself(nextseg);
        while (nextseg.sh != dummysh) {
          setshelltype(nextseg, SHARP);
          // Adjust the direction of nextseg.
          nextseg.shver = 0;
          if (sorg(nextseg) != edest) {
            sesymself(nextseg);
          }
          //assert(sorg(nextseg) == edest);
          edest = sdest(nextseg);
          // Go the next connected subsegment at edest.
          senextself(nextseg);
          spivotself(nextseg);
        }
        sharpsegcount++;
      }
    }
    segloop.sh = shellfacetraverse(subsegs);
  }

  // So far we have marked all segments which have an acute dihedral angle
  //   or whose ORIGINs have an acute angle. In the un-marked subsegments,
  //   there are possible ones whose DESTINATIONs have an acute angle.
  subsegs->traversalinit();
  segloop.sh = shellfacetraverse(subsegs);
  while (segloop.sh != (shellface *) NULL) {
    // Only operate if s is non-sharp and contains the dest.
    segloop.shver = 0;
    senext(segloop, nextseg);
    spivotself(nextseg);
    // if ((nextseg.sh == dummysh) && (shelltype(segloop) != SHARP)) {
    if (nextseg.sh == dummysh) {
      // issharp = false;
      issharp = (shelltype(segloop) == SHARP);
      spivot(segloop, startsh);
      if (startsh.sh != dummysh) {
        // Check if s forms an acute angle with another seg.
        eorg = sdest(segloop);
        spinsh = startsh;
        do {
          if (sorg(spinsh) != eorg) {
            sesymself(spinsh);
          }
          // Calculate the angle between s and s' of this facet.
          neighsh = spinsh;
          ang = 0.0;
          do {
            ang += interiorangle(eorg, sdest(neighsh), sapex(neighsh), NULL);
            senext2self(neighsh);
            sspivot(neighsh, nextseg);
            if (nextseg.sh != dummysh) break;
            // Go to the next coplanar subface.
            spivotself(neighsh);
            //assert(neighsh.sh != dummysh);
            if (sorg(neighsh) != eorg) {
              sesymself(neighsh);
            }
          } while (true);
          // Only do check if a sharp angle has not been found.
          if (!issharp) issharp = (ang < smallang);
          // Remember the smallest input face angle.
          minfaceang = minfaceang < ang ? minfaceang : ang;
          // Go to the next facet.
          spivotself(spinsh);
          if (spinsh.sh == dummysh) break; // A single subface case.
        } while (spinsh.sh != startsh.sh);
      }
      if (issharp) {
        setshelltype(segloop, SHARP);
        // Set the type for all subsegments at backwards.
        eorg = sorg(segloop);
        senext2(segloop, prevseg);
        spivotself(prevseg);
        while (prevseg.sh != dummysh) {
          setshelltype(prevseg, SHARP);
          // Adjust the direction of prevseg.
          prevseg.shver = 0;
          if (sdest(prevseg) != eorg) {
            sesymself(prevseg);
          }
          //assert(sdest(prevseg) == eorg);
          eorg = sorg(prevseg);
          // Go to the next connected subsegment at eorg.
          senext2self(prevseg);
          spivotself(prevseg);
        }
        sharpsegcount++;
      }
    }
    segloop.sh = shellfacetraverse(subsegs);
  } 

  if ((b->verbose > 0) && (sharpsegcount > 0)) {
    //printf("  %d sharp segments.\n", sharpsegcount);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// decidefeaturepointsizes()    Decide the sizes for all feature points.     //
//                                                                           //
// A feature point is a point on a sharp segment. Every feature point p will //
// be assigned a positive size which is the radius of the protecting ball.   //
//                                                                           //
// The size of a feature point may be specified by one of the following ways://
//   (1) directly specifying on an input vertex (by using .mtr file);        //
//   (2) imposing a fixed maximal volume constraint ('-a__' option);         //
//   (3) imposing a maximal volume constraint in a region ('-a' option);     //
//   (4) imposing a maximal area constraint on a facet (in .var file);       //
//   (5) imposing a maximal length constraint on a segment (in .var file);   //
//   (6) combining (1) - (5).                                                //
//   (7) automatically deriving a size if none of (1) - (6) is available.    //
// In case (7),the size of p is set to be the smallest edge length among all //
// edges connecting at p.  The final size of p is the minimum of (1) - (7).  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::decidefeaturepointsizes()
{
  list *tetlist, *verlist;
  shellface **segsperverlist;
  triface starttet;
  face shloop;
  face checkseg, prevseg, nextseg, testseg;
  point ploop, adjpt, e1, e2;
  REAL lfs_0, len, vol, maxlen, varlen;
  bool isfeature;
  int *idx2seglist;
  int featurecount;
  int idx, i, j;

  if (b->verbose > 0) {
    //printf("  Deciding feature-point sizes.\n");
  }

  // Constructing a map from vertices to segments.
  makesegmentmap(idx2seglist, segsperverlist);
  // Initialize working lists.
  tetlist = new list(sizeof(triface), NULL, 256);
  verlist = new list(sizeof(point *), NULL, 256);

  if (b->fixedvolume) {
    // A fixed volume constraint is imposed. This gives an upper bound of
    //   the maximal radius of the protect ball of a vertex.
    maxlen = pow(6.0 * b->maxvolume, 1.0/3.0);
  }

  // First only assign a size of p if p is not a Steiner point. The size of
  //   a Steiner point will be interpolated later from the endpoints of the
  //   segment on which it lies. 
  featurecount = 0;
  points->traversalinit();
  ploop = pointtraverse();
  while (ploop != (point) NULL) {
    if (pointtype(ploop) != FREESEGVERTEX) {
      // Is p a feature point?
      isfeature = false;
      idx = pointmark(ploop) - in->firstnumber;
      for (i = idx2seglist[idx]; i < idx2seglist[idx + 1] && !isfeature; i++) {
        checkseg.sh = segsperverlist[i];
        isfeature = (shelltype(checkseg) == SHARP);
      }
      // Decide the size of p if it is on a sharp segment.
      if (isfeature) {
        // Find a tet containing p;
        sstpivot(&checkseg, &starttet);
        // Form star(p).
        tetlist->append(&starttet);
        formstarpolyhedron(ploop, tetlist, verlist, true);
        // Decide the size for p if no input size is given on input.
        if (ploop[pointmtrindex] == 0.0) {
          // Calculate lfs_0(p).
          lfs_0 = longest;
          for (i = 0; i < verlist->len(); i++) {
            adjpt = * (point *)(* verlist)[i];
            if (pointtype(adjpt) == FREESEGVERTEX) {
              // A Steiner point q. Find the seg it lies on.
              sdecode(point2seg(adjpt), checkseg);
              //assert(checkseg.sh != dummysh);
              checkseg.shver = 0;
              // Find the origin of this seg.
              prevseg = checkseg;
              e1 = sorg(prevseg);
              do {
                senext2(prevseg, testseg);
                spivotself(testseg);
                if (testseg.sh == dummysh) break;
                // Go to the previous subseg.
                prevseg = testseg; 
                // Adjust the direction of the previous subsegment.
                prevseg.shver = 0;
                if (sdest(prevseg) != e1) {
                  sesymself(prevseg);
                }
                //assert(sdest(prevseg) == e1);
                e1 = sorg(prevseg);
              } while (true);
              // Find the dest of this seg.
              nextseg = checkseg;
              e2 = sdest(nextseg);
              do {
                senext(nextseg, testseg);
                spivotself(testseg);
                if (testseg.sh == dummysh) break;
                // Go to the next subseg.
                nextseg = testseg;
                // Adjust the direction of the nextseg.
                nextseg.shver = 0;
                if (sorg(nextseg) != e2) {
                  sesymself(nextseg);
                }
                //assert(sorg(nextseg) == e2);
                e2 = sdest(nextseg);
              } while (true);
              // e1 = sorg(prevseg);
              // e2 = sdest(nextseg);
              // Check if p is the origin or the dest of this seg.
              if (ploop == e1) {
                // Set q to be the dest of this seg.
                adjpt = e2;
              } else if (ploop == e2) {
                // Set q to be the org of this seg.
                adjpt = e1;
              }
            }
            len = distance(ploop, adjpt);
            if (lfs_0 > len) lfs_0 = len;
          }
          ploop[pointmtrindex] = lfs_0;
        }
        if (b->fixedvolume) {
          // A fixed volume constraint is imposed. Adjust H(p) <= maxlen.
          if (ploop[pointmtrindex] > maxlen) {
            ploop[pointmtrindex] = maxlen;
          }
        }
        if (b->varvolume) {
          // Variant volume constraints are imposed. Adjust H(p) <= varlen.
          for (i = 0; i < tetlist->len(); i++) {
            starttet = * (triface *)(* tetlist)[i];
            vol = volumebound(starttet.tet);
            if (vol > 0.0) {
              varlen = pow(6 * vol, 1.0/3.0);
              if (ploop[pointmtrindex] > varlen) {
                ploop[pointmtrindex] = varlen;
              }
            }
          }
        }
        // Clear working lists.
        tetlist->clear();
        verlist->clear();
        featurecount++;
      } else {
        // NO feature point, set the size of p be zero.
        ploop[pointmtrindex] = 0.0;
      }
    } // if (pointtype(ploop) != FREESEGVERTEX) {
    ploop = pointtraverse();
  }

  if (b->verbose > 1) {
    //printf("  %d feature points.\n", featurecount);
  }

  if (!b->refine) {
    // Second only assign sizes for all Steiner points. A Steiner point p
    //   inserted on a sharp segment s is assigned a size by interpolating
    //   the sizes of the original endpoints of s.
    featurecount = 0;
    points->traversalinit();
    ploop = pointtraverse();
    while (ploop != (point) NULL) {
      if (pointtype(ploop) == FREESEGVERTEX) {
        if (ploop[pointmtrindex] == 0.0) {
          sdecode(point2seg(ploop), checkseg);
          //assert(checkseg.sh != dummysh);
          if (shelltype(checkseg) == SHARP) {
            checkseg.shver = 0;
            // Find the origin of this seg.
            prevseg = checkseg;
            e1 = sorg(prevseg);
            do {
              senext2(prevseg, testseg);
              spivotself(testseg);
              if (testseg.sh == dummysh) break;
              prevseg = testseg; // Go the previous subseg.
              // Adjust the direction of this subsegmnt.
              prevseg.shver = 0;
              if (sdest(prevseg) != e1) {
                sesymself(prevseg);
              }
              //assert(sdest(prevseg) == e1);
              e1 = sorg(prevseg);
            } while (true);
            // Find the dest of this seg.
            nextseg = checkseg;
            e2 = sdest(nextseg);
            do {
              senext(nextseg, testseg);
              spivotself(testseg);
              if (testseg.sh == dummysh) break;
              nextseg = testseg; // Go the next subseg.
              // Adjust the direction of this subsegment.
              nextseg.shver = 0;
              if (sorg(nextseg) != e2) {
                sesymself(nextseg);
              }
              //assert(sorg(nextseg) == e2);
              e2 = sdest(nextseg);
            } while (true);
            // e1 = sorg(prevseg);
            // e2 = sdest(nextseg);
            len = distance(e1, e2);
            lfs_0 = distance(e1, ploop);
            // The following //assert() happens when -Y option is used.
            if (b->nobisect == 0) {
              //assert(lfs_0 < len); 
            }
            ploop[pointmtrindex] = e1[pointmtrindex]
              + (lfs_0 / len) * (e2[pointmtrindex] - e1[pointmtrindex]);
            featurecount++;
          } else {
            // NO feature point, set the size of p be zero.
            ploop[pointmtrindex] = 0.0;
          } // if (shelltype(checkseg) == SHARP)
        } // if (ploop[pointmtrindex] == 0.0)
      } // if (pointtype(ploop) != FREESEGVERTEX)
      ploop = pointtraverse();
    }
    if ((b->verbose > 1) && (featurecount > 0)) {
      //printf("  %d Steiner feature points.\n", featurecount);
    }
  }

  if (varconstraint) {
    // A .var file exists. Adjust feature sizes.
    if (in->facetconstraintlist) {
      // Have facet area constrains.
      subfaces->traversalinit();
      shloop.sh = shellfacetraverse(subfaces);
      while (shloop.sh != (shellface *) NULL) {
        varlen = areabound(shloop);
        if (varlen > 0.0) {
          // Check if the three corners are feature points.
          varlen = sqrt(varlen);
          for (j = 0; j < 3; j++) {
            ploop = (point) shloop.sh[3 + j];
            isfeature = false;
            idx = pointmark(ploop) - in->firstnumber;
            for (i = idx2seglist[idx]; i < idx2seglist[idx + 1] && !isfeature; 
                 i++) {
              checkseg.sh = segsperverlist[i];
              isfeature = (shelltype(checkseg) == SHARP);
            }
            if (isfeature) {
              //assert(ploop[pointmtrindex] > 0.0);
              if (ploop[pointmtrindex] > varlen) {
                ploop[pointmtrindex] = varlen;
              }
            }
          } // for (j = 0; j < 3; j++)
        }
        shloop.sh = shellfacetraverse(subfaces);
      }
    }
    if (in->segmentconstraintlist) {
      // Have facet area constrains.
      subsegs->traversalinit();
      shloop.sh = shellfacetraverse(subsegs);
      while (shloop.sh != (shellface *) NULL) {
        varlen = areabound(shloop);
        if (varlen > 0.0) {
          // Check if the two endpoints are feature points.
          for (j = 0; j < 2; j++) {
            ploop = (point) shloop.sh[3 + j];
            isfeature = false;
            idx = pointmark(ploop) - in->firstnumber;
            for (i = idx2seglist[idx]; i < idx2seglist[idx + 1] && !isfeature; 
                 i++) {
              checkseg.sh = segsperverlist[i];
              isfeature = (shelltype(checkseg) == SHARP);
            }
            if (isfeature) {
              //assert(ploop[pointmtrindex] > 0.0);
              if (ploop[pointmtrindex] > varlen) {
                ploop[pointmtrindex] = varlen;
              }
            }
          } // for (j = 0; j < 2; j++)
        }
        shloop.sh = shellfacetraverse(subsegs);
      }
    }
  } // if (varconstraint)

  delete [] segsperverlist;
  delete [] idx2seglist;
  delete tetlist;
  delete verlist;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// enqueueencsub()    Add an encroached subface into the queue.              //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::enqueueencsub(face* testsub, point encpt, int quenumber,
  REAL* cent)
{
  badface *encsub;
  int i;

  if (!smarktested(*testsub)) {
    if (!shell2badface(*testsub)) {
      encsub = (badface *) badsubfaces->alloc();
      encsub->ss = *testsub;
      encsub->forg = sorg(*testsub);
      encsub->fdest = sdest(*testsub);
      encsub->fapex = sapex(*testsub);
      encsub->foppo = (point) encpt;
      for (i = 0; i < 3; i++) encsub->cent[i] = cent[i];
      encsub->nextitem = (badface *) NULL;
      // Set the pointer of 'encsubseg' into 'testsub'.  It has two purposes:
      //   (1) We can regonize it is encroached; (2) It is uniquely queued.
      setshell2badface(encsub->ss, encsub);
      // Add the subface to the end of a queue (quenumber = 2, high priority).
      *subquetail[quenumber] = encsub;
      // Maintain a pointer to the NULL pointer at the end of the queue.
      subquetail[quenumber] = &encsub->nextitem;
      if (b->verbose > 2) {
        //printf("    Queuing subface (%d, %d, %d) [%d].\n", 
         // pointmark(encsub->forg), pointmark(encsub->fdest), 
         // pointmark(encsub->fapex), quenumber);
      }
    }
  } else {
    if (b->verbose > 2) {
      //printf("    Ignore an encroached subface (%d, %d, %d).\n", 
       // pointmark(sorg(*testsub)), pointmark(sdest(*testsub)), 
       // pointmark(sapex(*testsub)));
    }
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// dequeueencsub()    Remove an enc-subface from the front of the queue.     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

tetgenmesh::badface* tetgenmesh::dequeueencsub(int* pquenumber)
{
  badface *result;
  int quenumber;

  // Look for a nonempty queue.
  for (quenumber = 2; quenumber >= 0; quenumber--) {
    result = subquefront[quenumber];
    if (result != (badface *) NULL) {
      // Remove the badface from the queue.
      subquefront[quenumber] = result->nextitem;
      // Maintain a pointer to the NULL pointer at the end of the queue.
      if (subquefront[quenumber] == (badface *) NULL) {
        subquetail[quenumber] = &subquefront[quenumber];
      }
      *pquenumber = quenumber;
      return result;
    }
  }
  return (badface *) NULL;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// enqueuebadtet()    Add a tetrahedron into the queue.                      //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::enqueuebadtet(triface* testtet, REAL ratio2, REAL* cent)
{
  badface *newbadtet;
  int queuenumber;
  int i;

  // Allocate space for the bad tetrahedron.
  newbadtet = (badface *) badtetrahedrons->alloc();
  newbadtet->tt = *testtet;
  newbadtet->key = ratio2;
  if (cent != NULL) {
    for (i = 0; i < 3; i++) newbadtet->cent[i] = cent[i];
  } else {
    for (i = 0; i < 3; i++) newbadtet->cent[i] = 0.0;
  }
  newbadtet->forg = org(*testtet);
  newbadtet->fdest = dest(*testtet);
  newbadtet->fapex = apex(*testtet);
  newbadtet->foppo = oppo(*testtet);
  newbadtet->nextitem = (badface *) NULL;
  // Determine the appropriate queue to put the bad tetrahedron into.
  if (ratio2 > b->goodratio) {
    // queuenumber = (int) ((ratio2 - b->goodratio) / 0.5);
    queuenumber = (int) (64.0 - 64.0 / ratio2);
    // 'queuenumber' may overflow (negative) caused by a very large ratio.
    if ((queuenumber > 63) || (queuenumber < 0)) {
      queuenumber = 63;
    }
  } else {
    // It's not a bad ratio; put the tet in the lowest-priority queue.
    queuenumber = 0;
  }

  // Are we inserting into an empty queue?
  if (tetquefront[queuenumber] == (badface *) NULL) {
    // Yes. Will this become the highest-priority queue?
    if (queuenumber > firstnonemptyq) {
      // Yes, this is the highest-priority queue.
      nextnonemptyq[queuenumber] = firstnonemptyq;
      firstnonemptyq = queuenumber; 
    } else {
      // No. Find the queue with next higher priority.
      i = queuenumber + 1;
      while (tetquefront[i] == (badface *) NULL) {
        i++;
      }
      // Mark the newly nonempty queue as following a higher-priority queue.
      nextnonemptyq[queuenumber] = nextnonemptyq[i];
      nextnonemptyq[i] = queuenumber;
    }
    // Put the bad tetrahedron at the beginning of the (empty) queue.
    tetquefront[queuenumber] = newbadtet;
  } else {
    // Add the bad tetrahedron to the end of an already nonempty queue.
    tetquetail[queuenumber]->nextitem = newbadtet;
  }
  // Maintain a pointer to the last tetrahedron of the queue.
  tetquetail[queuenumber] = newbadtet;

  if (b->verbose > 2) {
    //printf("    Queueing bad tet: (%d, %d, %d, %d), ratio %g, qnum %d.\n",
          // pointmark(newbadtet->forg), pointmark(newbadtet->fdest),
          // pointmark(newbadtet->fapex), pointmark(newbadtet->foppo),
          // sqrt(ratio2), queuenumber);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// dequeuebadtet()    Remove a tetrahedron from the front of the queue.      //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

tetgenmesh::badface* tetgenmesh::topbadtetra()
{
  // Keep a record of which queue was accessed in case dequeuebadtetra()
  //   is called later.
  recentq = firstnonemptyq;
  // If no queues are nonempty, return NULL.
  if (firstnonemptyq < 0) {
    return (badface *) NULL;
  } else {
    // Return the first tetrahedron of the highest-priority queue.
    return tetquefront[firstnonemptyq];
  }
}

void tetgenmesh::dequeuebadtet()
{
  badface *deadbadtet;
  int i;

  // If queues were empty last time topbadtetra() was called, do nothing.
  if (recentq >= 0) {
    // Find the tetrahedron last returned by topbadtetra().
    deadbadtet = tetquefront[recentq];
    // Remove the tetrahedron from the queue.
    tetquefront[recentq] = deadbadtet->nextitem;
    // If this queue is now empty, update the list of nonempty queues.
    if (deadbadtet == tetquetail[recentq]) {
      // Was this the highest-priority queue?
      if (firstnonemptyq == recentq) {
        // Yes; find the queue with next lower priority.
        firstnonemptyq = nextnonemptyq[firstnonemptyq];
      } else {
        // No; find the queue with next higher priority.
        i = recentq + 1;
        while (tetquefront[i] == (badface *) NULL) {
          i++;
        }
        nextnonemptyq[i] = nextnonemptyq[recentq];
      }
    }
    // Return the bad tetrahedron to the pool.
    badfacedealloc(badtetrahedrons, deadbadtet);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// checkseg4encroach()    Check a subsegment to see if it is encroached.     //
//                                                                           //
// A segment s is encroached if there is a vertex lies inside or on its dia- //
// metral circumsphere, i.e., s faces an angle theta > 90 degrees.           //
//                                                                           //
// If 'testpt' (p) != NULL, only test if 'testseg' (s) is encroached by it,  //
// else, check all apexes of faces around s. Return TRUE if s is encroached. //
// If and 'enqflag' is TRUE, add it into 'badsubsegs' if s is encroached.    //
//                                                                           //
// If 'prefpt' != NULL, it returns the reference point (defined in my paper) //
// if it exists.  This point is will be used to split s.                     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::checkseg4encroach(face* testseg, point testpt, point* prefpt,
  bool enqflag)
{
  badface *encsubseg;
  triface starttet, spintet;
  point eorg, edest, eapex, encpt;
  REAL cent[3], radius, dist, diff;
  REAL maxradius;
  bool enq;
  int hitbdry;

  enq = false;
  eorg = sorg(*testseg);
  edest = sdest(*testseg);
  cent[0] = 0.5 * (eorg[0] + edest[0]);
  cent[1] = 0.5 * (eorg[1] + edest[1]);
  cent[2] = 0.5 * (eorg[2] + edest[2]);
  radius = distance(cent, eorg);

  if (varconstraint && (areabound(*testseg) > 0.0)) {
    enq = (2.0 * radius) > areabound(*testseg);
  }

  if (!enq) {
    maxradius = 0.0;
    if (testpt == (point) NULL) {
      // Check if it is encroached by traversing all faces containing it.
      sstpivot(testseg, &starttet);
      eapex = apex(starttet);
      spintet = starttet;
      hitbdry = 0;
      do {
        dist = distance(cent, apex(spintet));
        diff = dist - radius;
        if (fabs(diff) / radius <= b->epsilon) diff = 0.0; // Rounding.
        if (diff <= 0.0) {
          // s is encroached.
          enq = true;
          if (prefpt != (point *) NULL) {
            // Find the reference point.
            encpt = apex(spintet);
            circumsphere(eorg, edest, encpt, NULL, NULL, &dist);
            if (dist > maxradius) {
              // Rememebr this point.
              *prefpt = encpt;
              maxradius = dist;
            }
          } else {
            break;
          }
        }
        if (!fnextself(spintet)) {
          hitbdry++;
          if (hitbdry < 2) {
            esym(starttet, spintet);
            if (!fnextself(spintet)) {
              hitbdry++;
            } 
          }
        }
      } while (apex(spintet) != eapex && (hitbdry < 2));
    } else {
      // Only check if 'testseg' is encroached by 'testpt'.
      dist = distance(cent, testpt);
      diff = dist - radius;
      if (fabs(diff) / radius <= b->epsilon) diff = 0.0; // Rounding.
      enq = (diff <= 0.0);
    }
  }

  if (enq && enqflag) {
    // This segment is encroached and should be repaired. 
    if (!smarktested(*testseg)) {
      if (!shell2badface(*testseg)) { // Is it not queued yet?
        if (b->verbose > 2) {
          //printf("    Queuing encroaching subsegment (%d, %d).\n",
               //  pointmark(eorg), pointmark(edest));
        }
        encsubseg = (badface *) badsubsegs->alloc();
        encsubseg->ss = *testseg;
        encsubseg->forg = eorg;
        encsubseg->fdest = edest;
        encsubseg->foppo = (point) NULL; // Not used.
        // Set the pointer of 'encsubseg' into 'testseg'.  It has two purposes:
        //   (1) We can regonize it is encroached; (2) It is uniquely queued.
        setshell2badface(encsubseg->ss, encsubseg);
      }
    } else {
      // This segment has been rejected for splitting. Do not queue it.
      if (b->verbose > 2) {
        //printf("    Ignore a rejected encroaching subsegment (%d, %d).\n",
             //  pointmark(eorg), pointmark(edest));
      }
    }
  }
  
  return enq;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// checksub4encroach()    Check a subface to see if it is encroached.        //
//                                                                           //
// A subface f is encroached if there is a vertex inside or on its diametral //
// circumsphere.                                                             //
//                                                                           //
// If 'testpt (p) != NULL', test if 'testsub' (f) is encroached by it, else, //
// test if f is encroached by one of the two opposites of the adjacent tets. //
// Return TRUE if f is encroached and queue it if 'enqflag' is set.          //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::checksub4encroach(face* testsub, point testpt, bool enqflag)
{
  triface abuttet;
  point pa, pb, pc, encpt;
  REAL A[4][4], rhs[4], D;
  REAL cent[3], area;
  REAL radius, dist, diff;
  bool enq;
  int indx[4];
  int quenumber;

  enq = false;
  radius = 0.0;
  encpt = (point) NULL;

  pa = sorg(*testsub);
  pb = sdest(*testsub);
  pc = sapex(*testsub);
  
  // Compute the coefficient matrix A (3x3).
  A[0][0] = pb[0] - pa[0];
  A[0][1] = pb[1] - pa[1];
  A[0][2] = pb[2] - pa[2]; // vector V1 (pa->pb)
  A[1][0] = pc[0] - pa[0];
  A[1][1] = pc[1] - pa[1];
  A[1][2] = pc[2] - pa[2]; // vector V2 (pa->pc)
  cross(A[0], A[1], A[2]); // vector V3 (V1 X V2)

  if (varconstraint && (areabound(*testsub) > 0.0)) {
    // Check if the subface has too big area.
    area = 0.5 * sqrt(dot(A[2], A[2]));
    enq = area > areabound(*testsub);
    if (enq) {
      quenumber = 2; // A queue of subfaces having too big area.
    }
  }

  // Compute the right hand side vector b (3x1).
  rhs[0] = 0.5 * dot(A[0], A[0]);
  rhs[1] = 0.5 * dot(A[1], A[1]);
  rhs[2] = 0.0;
  // Solve the 3 by 3 equations use LU decomposition with partial pivoting
  //   and backward and forward substitute..
  if (lu_decmp(A, 3, indx, &D, 0)) {
    lu_solve(A, 3, indx, rhs, 0);
    cent[0] = pa[0] + rhs[0];
    cent[1] = pa[1] + rhs[1];
    cent[2] = pa[2] + rhs[2];
    radius = sqrt(rhs[0] * rhs[0] + rhs[1] * rhs[1] + rhs[2] * rhs[2]);
  }
  
  if (!enq) {
    // Check if the subface is encroached.
    if (testpt == (point) NULL) {
      stpivot(*testsub, abuttet);
      if (abuttet.tet != dummytet) {
        dist = distance(cent, oppo(abuttet));
        diff = dist - radius;
        if (fabs(diff) / radius <= b->epsilon) diff = 0.0; // Rounding.
        enq = (diff <= 0.0);
        if (enq) encpt = oppo(abuttet);
      }
      if (!enq) {
        sesymself(*testsub);
        stpivot(*testsub, abuttet);
        if (abuttet.tet != dummytet) {
          dist = distance(cent, oppo(abuttet));
          diff = dist - radius;
          if (fabs(diff) / radius <= b->epsilon) diff = 0.0; // Rounding.
          enq = (diff <= 0.0);
          if (enq) encpt = oppo(abuttet);
        }
      }
    } else {
      dist = distance(cent, testpt);
      diff = dist - radius;
      if (fabs(diff) / radius <= b->epsilon) diff = 0.0; // Rounding.
      enq = (diff <= 0.0);
    }
    if (enq) {
      quenumber = 0; // A queue of encroached subfaces.
    }
  }

  if (enq && enqflag) {
    enqueueencsub(testsub, encpt, quenumber, cent);
  }

  return enq;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// checktet4badqual()    Test a tetrahedron for quality measures.            //
//                                                                           //
// Tests a tetrahedron to see if it satisfies the minimum ratio condition    //
// and the maximum volume condition. Tetrahedra that aren't upto spec are    //
// added to the bad tetrahedron queue.                                       //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::checktet4badqual(triface* testtet, bool enqflag)
{
  point pa, pb, pc, pd, pe1, pe2;
  REAL vda[3], vdb[3], vdc[3];
  REAL vab[3], vbc[3], vca[3]; 
  REAL N[4][3], A[4][4], rhs[4], D;
  REAL elen[6], circumcent[3];
  REAL bicent[3], offcent[3];
  REAL volume, L, cosd;
  REAL radius2, smlen2, ratio2;
  REAL dist, sdist, split;
  bool enq;
  int indx[4];
  int sidx, i, j; 

  pa = (point) testtet->tet[4];
  pb = (point) testtet->tet[5];
  pc = (point) testtet->tet[6];
  pd = (point) testtet->tet[7];

  // Get the edge vectors vda: d->a, vdb: d->b, vdc: d->c.
  // Set the matrix A = [vda, vdb, vdc]^T.
  for (i = 0; i < 3; i++) A[0][i] = vda[i] = pa[i] - pd[i];
  for (i = 0; i < 3; i++) A[1][i] = vdb[i] = pb[i] - pd[i];
  for (i = 0; i < 3; i++) A[2][i] = vdc[i] = pc[i] - pd[i];
  // Get the rest edge vectors
  for (i = 0; i < 3; i++) vab[i] = pb[i] - pa[i];
  for (i = 0; i < 3; i++) vbc[i] = pc[i] - pb[i];
  for (i = 0; i < 3; i++) vca[i] = pa[i] - pc[i];

  // Lu-decompose the matrix A.
  lu_decmp(A, 3, indx, &D, 0);
  // Get the volume of abcd.
  volume = (A[indx[0]][0] * A[indx[1]][1] * A[indx[2]][2]) / 6.0;
  if (volume < 0.0) volume = -volume;
  // Check the radiu-edge ratio of the tet.
  rhs[0] = 0.5 * dot(vda, vda);
  rhs[1] = 0.5 * dot(vdb, vdb);
  rhs[2] = 0.5 * dot(vdc, vdc);
  lu_solve(A, 3, indx, rhs, 0);
  // Get the circumcenter.
  for (i = 0; i < 3; i++) circumcent[i] = pd[i] + rhs[i];
  // Get the square of the circumradius.
  radius2 = dot(rhs, rhs);
  // Find the square of the shortest edge length.
  elen[0] = dot(vda, vda);
  elen[1] = dot(vdb, vdb);
  elen[2] = dot(vdc, vdc);
  elen[3] = dot(vab, vab);
  elen[4] = dot(vbc, vbc);
  elen[5] = dot(vca, vca);
  smlen2 = elen[0]; sidx = 0;
  for (i = 1; i < 6; i++) {
    if (smlen2 > elen[i]) { smlen2 = elen[i]; sidx = i; }
  }
  // Calculate the square of radius-edge ratio.
  ratio2 = radius2 / smlen2;
  // Check whether the ratio is smaller than permitted.
  enq = ratio2 > b->goodratio;
  if (!enq) {
    // abcd has good ratio.
    // ratio2 = 0.0;
    // if (b->offcenter) {
      // Test if it is a sliver.
      // Compute the 4 face normals (N[0], ..., N[3]).
      for (j = 0; j < 3; j++) {
        for (i = 0; i < 3; i++) rhs[i] = 0.0;
        rhs[j] = 1.0;  // Positive means the inside direction
        lu_solve(A, 3, indx, rhs, 0);
        for (i = 0; i < 3; i++) N[j][i] = rhs[i];
      }
      // Get the fourth normal by summing up the first three.
      for (i = 0; i < 3; i++) N[3][i] = - N[0][i] - N[1][i] - N[2][i];
      // Normalized the normals.
      for (i = 0; i < 4; i++) {
        L = sqrt(dot(N[i], N[i]));
        if (L > 0.0) {
          for (j = 0; j < 3; j++) N[i][j] /= L;
        }
      }
      // N[0] is the normal of face bcd. Test the dihedral angles at edge
      //   cd, bd, and bc to see if they are too small or too big.
      for (i = 1; i < 4 && !enq; i++) {
        cosd = -dot(N[0], N[i]); // Edge cd, bd, bc.
        enq = cosd > cosmindihed;
      }
      if (!enq) {
        for (i = 2; i < 4 && !enq; i++) {
          cosd = -dot(N[1], N[i]); // Edge ad, ac
          enq = cosd > cosmindihed;
        }
        if (!enq) {
          cosd = -dot(N[2], N[3]); // Edge ab
          enq = cosd > cosmindihed;
        }
      }
    // }
  } else if (b->offcenter) {
    // abcd has bad-quality. Use off-center instead of circumcenter.
    switch (sidx) {
    case 0: // edge da.
      pe1 = pd; pe2 = pa; break;
    case 1: // edge db.
      pe1 = pd; pe2 = pb; break;
    case 2: // edge dc.
      pe1 = pd; pe2 = pc; break;
    case 3: // edge ab.
      pe1 = pa; pe2 = pb; break;
    case 4: // edge bc.
      pe1 = pb; pe2 = pc; break;
    case 5: // edge ca.
      pe1 = pc; pe2 = pa; break;
    default: 
      pe1 = pe2 = (point) NULL; // Avoid a compile warning.
    }
    // The shortest edge is e1->e2.
    for (i = 0; i < 3; i++) bicent[i] = 0.5 * (pe1[i] + pe2[i]);
    dist = distance(bicent, circumcent);
    // sdist = sqrt(smlen2) * sin(PI / 3.0); // A icoso-triangle.
    // The following formulae is from 
    sdist = b->alpha3 * (b->minratio+sqrt(b->goodratio-0.25))* sqrt(smlen2);
    split = sdist / dist;
    if (split > 1.0) split = 1.0;
    // Get the off-center.
    for (i = 0; i < 3; i++) {
      offcent[i] = bicent[i] + split * (circumcent[i] - bicent[i]);
    }
  }

  if (!enq && (b->varvolume || b->fixedvolume)) {
    // Check if the tet has too big volume.
    enq = b->fixedvolume && (volume > b->maxvolume);
    if (!enq && b->varvolume) {
      enq = (volume > volumebound(testtet->tet)) &&
            (volumebound(testtet->tet) > 0.0);
    }
  }

  if (!enq) {
    // Check if the user-defined sizing function is satisfied. 
    if (b->metric) {
      if (in->tetunsuitable != NULL) {
        // Execute the user-defined meshing sizing evaluation.
        pa = (point) testtet->tet[4];
        pb = (point) testtet->tet[5];
        pc = (point) testtet->tet[6];
        pd = (point) testtet->tet[7];
        enq = (*(in->tetunsuitable))(pa, pb, pc, pd, elen, volume);
      } else {
        // //assert(b->alpha1 > 0.0);
        sdist = sqrt(radius2) / b->alpha1;
        for (i = 0; i < 4; i++) {
          pa = (point) testtet->tet[4 + i];
          // Get the indicated size of p.
          dist = pa[pointmtrindex]; // dist = b->alpha1 * pa[pointmtrindex];
          enq = ((dist < sdist) && (dist > 0.0));
          if (enq) break; // It is bad wrt. a node constraint.
          // *** Experiment ! Stop test if c is inside H(a).
          // if ((dist > 0.0) && (dist > sdist)) break;
        }
      }
      // *** Experiment !
      // enq = (i == 4); // Does c lies outside all sparse-ball?
    } // if (b->metric)
  }

  if (enq && enqflag) {
    if (b->offcenter && (ratio2 > b->goodratio)) {
      for (i = 0; i < 3; i++) circumcent[i] = offcent[i];
    }
    enqueuebadtet(testtet, ratio2, circumcent);
  }

  return enq;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// acceptsegpt()    Check if a segment point can be inserted or not.         //
//                                                                           //
// Segment(ab) is indicated to be split by a point p (\in ab). This routine  //
// decides whether p can be inserted or not.                                 //
//                                                                           //
// p can not be inserted either the '-Y' option is used and ab is a hull     //
// segment or '-YY' option is used.                                          //
//                                                                           //
// p can be inserted if it is in one of the following cases:                 //
//   (1) if L = |a - b| is too long wrt the edge constraint; or              //
//   (2) if |x - p| > \alpha_2 H(x) for x = a, b; or                         //
//   (3) if 'refpt' != NULL.                                                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::acceptsegpt(point segpt, point refpt, face* splitseg)
{
  point p[2];
  REAL L, lfs;
  int i, j;

  // This segment must have not been checked (and rejected) yet.
  //assert(!smarktested(*splitseg));

  if (b->nobisect == 1) {
    // '-Y'. It can not be split if it is on the hull.
    triface spintet;
    point pc;
    sstpivot(splitseg, &spintet);
    //assert(spintet.tet != dummytet);
    pc = apex(spintet);
    do {
      if (!fnextself(spintet)) {
        // Meet a boundary face - s is on the hull.
        return false;
      }
    } while (pc != apex(spintet));
  } else if (b->nobisect > 1) {
    // '-YY'. Do not split it.
    return false;
  }
  
  p[0] = sorg(*splitseg);
  p[1] = sdest(*splitseg);
  if (varconstraint && (areabound(*splitseg) > 0)) {
    lfs = areabound(*splitseg);
    L = distance(p[0], p[1]);
    if (L > lfs) {
      return true; // case (1)
    }
  }

  j = 0; // Use j to count the number of inside balls.
  for (i = 0; i < 2; i++) {
    // Check if p is inside the protect ball of q.
    if (p[i][pointmtrindex] > 0.0) {
      lfs = b->alpha2 * p[i][pointmtrindex];
      L = distance(p[i], segpt);
      if (L < lfs) j++; // p is inside ball.
    }
  }
  if (j == 0) return true; // case (3).

  // If 'refpt' != NULL, force p to be inserted.
  if (refpt != (point) NULL) {
    cdtenforcesegpts++;
    return true;
  }

  // Do not split it.
  rejsegpts++;
  return false;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// acceptfacpt()    Check if a facet point can be inserted or not.           //
//                                                                           //
// 'subceillist' is CBC(p). 'verlist' (V) is empty on input, it returns the  //
// set of vertices of CBC(p).                                                //
//                                                                           //
// p can not be inserted either the '-Y' option is used and the facet is on  //
// the hull or '-YY' option is used.                                         //
//                                                                           //
// p can be inserted if |p - v| > \alpha_2 H(v), for all v \in V.            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::acceptfacpt(point facpt, list* subceillist, list* verlist)
{
  face *testsh;
  point p[2], ploop;
  REAL L, lfs;
  int idx, i, j;

  if (b->nobisect == 1) {
    // '-Y'. p can not be inserted if CBC(p) is on the hull.
    triface testtet;
    testsh = (face *)(* subceillist)[0];
    stpivot(*testsh, testtet);
    if (testtet.tet != dummytet) {
      sesymself(*testsh);
      stpivot(*testsh, testtet);
    }
    if (testtet.tet == dummytet) return false;
  } else if (b->nobisect > 1) {
    // '-YY'. Do not split s.
    return false;
  }

  // Collect the vertices of CBC(p), save them in V.
  for (i = 0; i < subceillist->len(); i++) {
    testsh = (face *)(* subceillist)[i];
    p[0] = sorg(*testsh);
    p[1] = sdest(*testsh);
    for (j = 0; j < 2; j++) {
      idx = pointmark(p[j]);
      if (idx >= 0) {
        setpointmark(p[j], -idx - 1);
        verlist->append(&(p[j]));
      }
    }
  }

  j = 0; // Use j to count the number of inside balls.
  for (i = 0; i < verlist->len(); i++) {
    ploop = * (point *)(* verlist)[i];
    // Uninfect q.
    idx = pointmark(ploop);
    setpointmark(ploop, -(idx + 1)); 
    // Check if p is inside the protect ball of q.
    if (ploop[pointmtrindex] > 0.0) {
      lfs = b->alpha2 * ploop[pointmtrindex];
      L = distance(ploop, facpt);
      if (L < lfs) j++; // p is inside ball.
    }
  }
  verlist->clear();

  if (j == 0) return true; // case (3).

  rejsubpts++;
  return false;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// acceptvolpt()    Check if a volume point can be inserted or not.          //
//                                                                           //
// 'ceillist' is B(p).  'verlist' (V) is empty on input, it returns the set  //
// of vertices of B(p).                                                      //
//                                                                           //
// p can be split if |p - v| > \alpha_2 H(v), for all v \in V.               //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::acceptvolpt(point volpt, list* ceillist, list* verlist)
{
  triface* testtet;
  point p[3], ploop;
  REAL L, lfs;
  int idx, i, j;

  // Collect the vertices of CBC(p), save them in V.
  for (i = 0; i < ceillist->len(); i++) {
    testtet = (triface *)(* ceillist)[i];
    p[0] = org(*testtet);
    p[1] = dest(*testtet);
    p[2] = apex(*testtet);
    for (j = 0; j < 3; j++) {
      idx = pointmark(p[j]);
      if (idx >= 0) {
        setpointmark(p[j], -idx - 1);
        verlist->append(&(p[j]));
      }
    }
  }

  j = 0; // Use j to counte the number of inside balls.
  for (i = 0; i < verlist->len(); i++) {
    ploop = * (point *)(* verlist)[i];
    // Uninfect q.
    idx = pointmark(ploop);
    setpointmark(ploop, -(idx + 1));
    // Check if p is inside the protect ball of q.
    if (ploop[pointmtrindex] > 0.0) {
      lfs = b->alpha2 * ploop[pointmtrindex];
      L = distance(ploop, volpt);
      if (L < lfs) j++; // p is inside the protect ball.
    }
  }
  verlist->clear();
  
  if (j == 0) return true; // case (2).

  rejtetpts++;
  return false;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// getsplitpoint()    Get the inserting point in a segment.                  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::getsplitpoint(point e1, point e2, point refpt, point newpt)
{
  point ei, ej;
  REAL split, L, d1, d2;
  bool acutea, acuteb;
  int i;

  if (refpt != (point) NULL) {
    // Use the CDT rules to split the segment.
    acutea = (pointtype(e1) == ACUTEVERTEX);
    acuteb = (pointtype(e2) == ACUTEVERTEX);
    if (acutea ^ acuteb) {
      // Only one endpoint is acute. Use rule-2 or rule-3.
      ei = acutea ? e1 : e2;
      ej = acutea ? e2 : e1;
      L = distance(ei, ej);
      // Apply rule-2.
      d1 = distance(ei, refpt);
      split = d1 / L;
      for (i = 0; i < 3; i++) newpt[i] = ei[i] + split * (ej[i] - ei[i]);
      // Check if rule-3 is needed.
      d2 = distance(refpt, newpt);
      if (d2 > (L - d1)) {
        // Apply rule-3.
        if ((d1 - d2) > (0.5 * d1)) {
          split = (d1 - d2) / L;
        } else {
          split = 0.5 * d1 / L;
        }
        for (i = 0; i < 3; i++) newpt[i] = ei[i] + split * (ej[i] - ei[i]);
        if (b->verbose > 1) {
          //printf("    Found by rule-3:");
        }
        r3count++;
      } else {
        if (b->verbose > 1) {
          //printf("    Found by rule-2:");
        }
        r2count++;
      }
      if (b->verbose > 1) {
        //printf(" center %d, split = %.12g.\n", pointmark(ei), split);
      }
    } else {
      // Both endpoints are acute or not. Split it at the middle.
      for (i = 0; i < 3; i++) newpt[i] = 0.5 * (e1[i] + e2[i]);
    }
  } else {
    // Split the segment at its midpoint.
    for (i = 0; i < 3; i++) newpt[i] = 0.5 * (e1[i] + e2[i]);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// setnewpointsize()    Set the size for a new point.                        //
//                                                                           //
// The size of the new point p is interpolated either from a background mesh //
// (b->bgmesh) or from the two input endpoints.                              //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::setnewpointsize(point newpt, point e1, point e2)
{
  if (b->metric) {
    // Interpolate the point size in a background mesh.
    triface bgmtet;
    // Get a tet in background mesh for locating p.
    decode(point2bgmtet(e1), bgmtet);
    p1interpolatebgm(newpt, &bgmtet, NULL);
  } else {
    if (e2 != (point) NULL) {
      // Interpolate the size between the two endpoints.
      REAL split, l, d;
      l = distance(e1, e2);
      d = distance(e1, newpt);
      split = d / l;
#ifdef SELF_CHECK
      // Check if e1 and e2 are endpoints of a sharp segment.
      //assert(e1[pointmtrindex] > 0.0);
      //assert(e2[pointmtrindex] > 0.0);
#endif
      newpt[pointmtrindex] = (1.0 - split) * e1[pointmtrindex] 
                           + split * e2[pointmtrindex];
    }
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// splitencseg()    Split an enc-seg and recover the Delaunayness by flips.  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::splitencseg(point newpt, face* splitseg, list* tetlist,
  list* sublist, list* verlist, queue* flipque, bool chkencsub, bool chkbadtet,
  bool optflag)
{
  list *mytetlist;
  queue *myflipque;
  triface starttet;
  face startsh, spinsh, checksh;
  int i;

  if (optflag) {
    mytetlist = new list(sizeof(triface), NULL, 1024);
    myflipque = new queue(sizeof(badface));
    tetlist = mytetlist;
    flipque = myflipque;
  }

  // Use the base orientation (important in this routine).
  splitseg->shver = 0;
  // Insert p, this should always success.
  sstpivot(splitseg, &starttet);
  if (splittetedge(newpt, &starttet, flipque)) {
    // Remove locally non-Delaunay faces by flipping.
    lawson3d(flipque);
  } else {
    if (optflag) {
      delete mytetlist;
      delete myflipque;
    }
    return false;
  }

  if (!optflag) { 
    // Check the two new subsegs to see if they're encroached (not by p).
    for (i = 0; i < 2; i++) {
      //if (!shell2badface(*splitseg)) {
        checkseg4encroach(splitseg, NULL, NULL, true);
      //}
      if (i == 1) break; // Two new segs have been checked.
      senextself(*splitseg);
      spivotself(*splitseg);
#ifdef SELF_CHECK
      //assert(splitseg->sh != (shellface *) NULL);
#endif
      splitseg->shver = 0;
    }
    // Check the new subfaces to see if they're encroached (not by p).
    if (chkencsub) {
      spivot(*splitseg, startsh);
      spinsh = startsh;
      do {
        sublist->append(&spinsh);
        formstarpolygon(newpt, sublist, verlist);
        for (i = 0; i < sublist->len(); i++) {
          checksh = * (face *)(* sublist)[i];
          //if (!shell2badface(checksh)) {
            checksub4encroach(&checksh, NULL, true);
          //}
        }
        sublist->clear();
        if (verlist) verlist->clear();
        spivotself(spinsh);
        if (spinsh.sh == dummysh) {
          break; // There's only one facet having this segment.
        }
      } while (spinsh.sh != startsh.sh);
    }
  } // if (!optflag)  

  // Collect the new tets connecting at p.
  sstpivot(splitseg, &starttet);
  tetlist->append(&starttet);
  formstarpolyhedron(newpt, tetlist, verlist, true);

  if (!optflag) {
    // Check if p encroaches adjacent segments.
    tallencsegs(newpt, 1, &tetlist);
    if (chkencsub) {
      // Check if p encroaches adjacent subfaces.
      tallencsubs(newpt, 1, &tetlist);
    }
    if (chkbadtet) {
      // Check if there are new bad quality tets at p.
      for (i = 0; i < tetlist->len(); i++) {
        starttet = * (triface *)(* tetlist)[i];
        checktet4badqual(&starttet, true);
      }
    }
    tetlist->clear();
  } else {
    // Check if new tets are non-optimal.
    for (i = 0; i < tetlist->len(); i++) {
      starttet = * (triface *)(* tetlist)[i];
      checktet4opt(&starttet, true);
    }
    delete mytetlist;
    delete myflipque;
  }

  return true;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// tallencsegs()    Check for encroached segments and save them in list.     //
//                                                                           //
// If 'testpt' (p) != NULL, only check if segments are encroached by p, else,//
// check all the nearby mesh vertices.                                       //
//                                                                           //
// If 'ceillists' (B_i(p)) != NULL, there are 'n' B_i(p)s, only check the    //
// segments which are on B_i(p)s, else, check the entire list of segments    //
// (in the pool 'this->subsegs').                                            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::tallencsegs(point testpt, int n, list **ceillists)
{
  list *ceillist;
  triface ceiltet;
  face checkseg;
  int enccount; // long oldencnum;
  int i, j, k;
  
  // Remember the current number of encroached segments.
  // oldencnum = badsubsegs->items;

  // Count the number of encroached segments.
  enccount = 0;

  if (ceillists != (list **) NULL) {
    for (k = 0; k < n; k++) {
      ceillist = ceillists[k];
      // Check the segments on B_i(p).
      for (i = 0; i < ceillist->len(); i++) {
        ceiltet = * (triface *)(* ceillist)[i];
        ceiltet.ver = 0;
        for (j = 0; j < 3; j++) {
          tsspivot(&ceiltet, &checkseg);
          if (checkseg.sh != dummysh) {
            // Found a segment. Test it if it isn't in enc-list.
            // if (!shell2badface(checkseg)) {
              if (checkseg4encroach(&checkseg, testpt, NULL, true)) {
                enccount++;
              }
            // }
          }
          enextself(ceiltet);
        }
      }
    }
  } else {
    // Check the entire list of segments.
    subsegs->traversalinit();
    checkseg.sh = shellfacetraverse(subsegs);
    while (checkseg.sh != (shellface *) NULL) {
      // Test it if it isn't in enc-list.
      // if (!shell2badface(checkseg)) {
        if (checkseg4encroach(&checkseg, testpt, NULL, true)) {
          enccount++;
        }
      // }
      checkseg.sh = shellfacetraverse(subsegs);
    }
  }

  // return (badsubsegs->items > oldencnum);
  return enccount > 0;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// tallencsubs()    Find all encroached subfaces and save them in list.      //
//                                                                           //
// If 'testpt' (p) != NULL, only check if subfaces are encroached by p, else,//
// check the adjacent vertices of subfaces.                                  //
//                                                                           //
// If 'ceillists' (B_i(p)) != NULL, there are 'n' B_i(p)s, only check the    //
// subfaces which are on B_i(p)s, else, check the entire list of subfaces    //
// (in the pool 'this->subfaces').                                           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::tallencsubs(point testpt, int n, list** ceillists)
{
  list *ceillist;
  triface ceiltet;
  face checksh;
  int enccount; //long oldencnum;
  int i, k;
  
  // Remember the current number of encroached segments.
  // oldencnum = badsubfaces->items;

  enccount = 0; // Count the number encroached subfaces.

  if (ceillists != (list **) NULL) {
    for (k = 0; k < n; k++) {
      ceillist = ceillists[k];
      // Check the subfaces on B_i(p).
      for (i = 0; i < ceillist->len(); i++) {
        ceiltet = * (triface *)(* ceillist)[i];
        tspivot(ceiltet, checksh);
        if (checksh.sh != dummysh) {
          // Found a subface. Test it if it isn't in enc-list.
          //if (!shell2badface(checksh)) {
            if (checksub4encroach(&checksh, testpt, true)) {
              enccount++;
            }
          //}
        }
      }
    }
  } else {
    // Check the entire list of subfaces.
    subfaces->traversalinit();
    checksh.sh = shellfacetraverse(subfaces);
    while (checksh.sh != (shellface *) NULL) {
      // Test it if it isn't in enc-list.
      // if (!shell2badface(checksh)) {
        if (checksub4encroach(&checksh, testpt, true)) {
          enccount++;
        }
      // }
      checksh.sh = shellfacetraverse(subfaces);
    }
  }

  //return (badsubfaces->items > oldencnum);
  return enccount > 0;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// tallbadtetrahedrons()    Queue all the bad-quality tetrahedra in the mesh.//
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::tallbadtetrahedrons()
{
  triface tetloop;

  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  while (tetloop.tet != (tetrahedron *) NULL) {
    checktet4badqual(&tetloop, true);
    tetloop.tet = tetrahedrontraverse();
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// repairencsegs()    Repair (split) all the encroached segments.            //
//                                                                           //
// Each encroached segment is repaired by splitting it - inserting a vertex  //
// at or near its midpoint.  Newly inserted vertices may encroach upon other //
// subsegments, these are also repaired.                                     //
//                                                                           //
// 'chkencsub' and 'chkbadtet' are two flags that specify whether one should //
// take note of new encroaced subfaces and bad quality tets that result from //
// inserting vertices to repair encroached subsegments.                      //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::repairencsegs(bool chkencsub, bool chkbadtet)
{
  list **tetlists, **ceillists;
  list **sublists, **subceillists;
  list *tetlist, *sublist;
  queue *flipque;
  badface *encloop;
  face splitseg;
  point newpt, refpt;
  point e1, e2;
  int nmax, n;

  n = 0;
  nmax = 128;
  if (!b->fliprepair) {
    tetlists = new list*[nmax];
    ceillists = new list*[nmax];
    sublists = new list*[nmax];
    subceillists = new list*[nmax];
  } else {
    tetlist = new list(sizeof(triface), NULL, 1024);
    sublist = new list(sizeof(face), NULL, 256);
    flipque = new queue(sizeof(badface));
  }

  // Loop until the pool 'badsubsegs' is empty. Note that steinerleft == -1
  //   if an unlimited number of Steiner points is allowed.
  while ((badsubsegs->items > 0) && (steinerleft != 0)) {
    badsubsegs->traversalinit();
    encloop = badfacetraverse(badsubsegs);
    while ((encloop != (badface *) NULL) && (steinerleft != 0)) {
      // Get an encroached subsegment s.
      splitseg = encloop->ss;
      // Clear the in-queue flag in s.
      setshell2badface(splitseg, NULL);
      if ((sorg(splitseg) == encloop->forg) && 
          (sdest(splitseg) == encloop->fdest)) {
        if (b->verbose > 1) {
          //printf("  Get an enc-seg (%d, %d)\n", pointmark(encloop->forg),
                // pointmark(encloop->fdest));
        }
        refpt = (point) NULL;
        if (b->conformdel) {
          // Look for a reference point.
          checkseg4encroach(&splitseg, NULL, &refpt, false);
        }
        // Create the new point p (at the middle of s).
        makepoint(&newpt);
        getsplitpoint(encloop->forg, encloop->fdest, refpt, newpt);
        setpointtype(newpt, FREESEGVERTEX);
        setpoint2seg(newpt, sencode(splitseg));
        // Decide whether p can be inserted or not.
        if (acceptsegpt(newpt, refpt, &splitseg)) {
          // Save the endpoints of the seg for size interpolation.
          e1 = sorg(splitseg);
          if (shelltype(splitseg) == SHARP) {
            e2 = sdest(splitseg);
          } else {
            e2 = (point) NULL; // No need to do size interoplation.
          }
          if (!b->fliprepair) {
            // Form BC(p), B(p), CBC(p)s, and C(p)s.
            formbowatcavity(newpt, &splitseg, NULL, &n, &nmax, sublists,
                            subceillists, tetlists, ceillists);
            // Validate/update BC(p), B(p), CBC(p)s, and C(p)s.
            if (trimbowatcavity(newpt, &splitseg, n, sublists, subceillists, 
                                tetlists, ceillists, -1.0)) {
              bowatinsertsite(newpt, &splitseg, n, sublists, subceillists,
                              tetlists, ceillists, NULL, flipque, true,
                              chkencsub, chkbadtet);
              setnewpointsize(newpt, e1, e2);
              if (steinerleft > 0) steinerleft--;
            } else {
              // p did not insert for invalid B(p).
              pointdealloc(newpt);
            }
            // Free the memory allocated in formbowatcavity().
            releasebowatcavity(&splitseg, n, sublists, subceillists, tetlists,
                               ceillists);
          } else {
            if (splitencseg(newpt, &splitseg, tetlist, sublist, NULL, flipque,
                        chkencsub, chkbadtet, false)) {
              setnewpointsize(newpt, e1, e2);
              if (steinerleft > 0) steinerleft--;
            } else {
              // Fail to split the segment. It MUST be caused by a very flat
              //   tet connected at the splitting segment. We do not handle
              //   this case yet. Hopefully, the later repairs will remove
              //   the flat tet and hence the segment can be split later.
              pointdealloc(newpt);
            }
          }
        } else {
          // This segment can not be split for not meeting the rules in
          //   acceptsegpt(). Mark it to avoid re-checking it later.
          smarktest(splitseg);
          // p did not accept for insertion.
          pointdealloc(newpt);
        } // if (checkseg4splitting(newpt, &splitseg))
      } // if ((encloop->forg == pa) && (encloop->fdest == pb))
      badfacedealloc(badsubsegs, encloop); // Remove this entry from list.
      encloop = badfacetraverse(badsubsegs); // Get the next enc-segment.
    } // while ((encloop != (badface *) NULL) && (steinerleft != 0))
  } // while ((badsubsegs->items > 0) && (steinerleft != 0))

  if (!b->fliprepair) {
    delete [] tetlists;
    delete [] ceillists;
    delete [] sublists;
    delete [] subceillists;
  } else {
    delete tetlist;
    delete sublist;
    delete flipque;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// repairencsubs()    Repair (split) all the encroached subfaces.            //
//                                                                           //
// Each encroached subface is repaired by splitting it - inserting a vertex  //
// at or near its circumcenter.  Newly inserted vertices may encroach upon   //
// other subfaces, these are also repaired.                                  //
//                                                                           //
// 'chkbadtet' is a flag that specifies whether one should take note of new  //
// bad quality tets that result from inserted vertices.                      //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::repairencsubs(bool chkbadtet)
{
  list *tetlists[2], *ceillists[2];
  list *sublist, *subceillist;
  list *verlist;
  badface *encloop;
  face splitsub, symsplitsub;
  point newpt, e1;
  enum locateresult loc;
  REAL normal[3], len;
  bool reject;
  long oldptnum, oldencsegnum;
  int quenumber, n, i;

  n = 0;
  sublist = (list *) NULL;
  subceillist = (list *) NULL;
  verlist = new list(sizeof(point *), NULL, 256);

  // Loop until the pool 'badsubfaces' is empty. Note that steinerleft == -1
  //   if an unlimited number of Steiner points is allowed.
  while ((badsubfaces->items > 0) && (steinerleft != 0)) {
    // Get an encroached subface f.
    encloop = dequeueencsub(&quenumber);
    splitsub = encloop->ss;
    // Clear the in-queue flag of f.
    setshell2badface(splitsub, NULL);
    // f may not be the same one when it was determined to be encroached.
    if (!isdead(&splitsub)
        && (sorg(splitsub) == encloop->forg)
        && (sdest(splitsub) == encloop->fdest)
        && (sapex(splitsub) == encloop->fapex)) {
      if (b->verbose > 1) {
        //printf("    Dequeuing ensub (%d, %d, %d) [%d].\n",
               //pointmark(encloop->forg), pointmark(encloop->fdest),
               //pointmark(encloop->fapex), quenumber);
      }
      // Create a new point p at the circumcenter of f.
      makepoint(&newpt);
      for (i = 0; i < 3; i++) newpt[i] = encloop->cent[i];
      setpointtype(newpt, FREESUBVERTEX);
      setpoint2sh(newpt, sencode(splitsub));
      // Set the abovepoint of f for point location.
      abovepoint = facetabovepointarray[shellmark(splitsub)];
      if (abovepoint == (point) NULL) {
        // getfacetabovepoint(&splitsub);
        // Calculate an abovepoint in dummypoint.
        facenormal2(encloop->forg, encloop->fdest, encloop->fapex, normal, 1);
        len = sqrt(DOT(normal, normal));
        normal[0] /= len;
        normal[1] /= len;
        normal[2] /= len;
        len = DIST(encloop->forg, encloop->fdest);
        len += DIST(encloop->fdest, encloop->fapex);
        len += DIST(encloop->fapex, encloop->forg);
        len /= 3.0;
        dummypoint[0] = encloop->forg[0] + len * normal[0];
        dummypoint[1] = encloop->forg[1] + len * normal[1];
        dummypoint[2] = encloop->forg[2] + len * normal[2];
        abovepoint = dummypoint;
      }
      // Locate p, start from f, stop at segment (1), use a tolerance to
      //   detect ONVERTEX or OUTSIDE case. Update f on return.
      loc = locatesub(newpt, &splitsub, 1, b->epsilon * 1e+2);
      if ((loc != ONVERTEX) && (loc != OUTSIDE)) {
        // Form BC(p), B(p), CBC(p) and C(p).
        formbowatcavity(newpt, NULL, &splitsub, &n, NULL, &sublist,
                        &subceillist, tetlists, ceillists);
        // Check for encroached subsegments (on B(p)).
        oldencsegnum = badsubsegs->items;
        reject = tallencsegs(newpt, 2, ceillists);
        if (reject && (oldencsegnum == badsubsegs->items)) {
          // 'newpt' encroaches upon some subsegments. But none of them can
          //    be split. So this subface can't be split as well. Mark it to
          //    avoid re-checking it later.
          smarktest(encloop->ss);
        }
        // Execute point accept rule if p does not encroach upon any segment.
        if (!reject) {
          reject = !acceptfacpt(newpt, subceillist, verlist);
          if (reject) {
            // 'newpt' lies in some protecting balls. This subface can't be
            //    split. Mark it to avoid re-checking it later.
            smarktest(encloop->ss);
          }
        }
        if (!reject) {
          // Validate/update cavity.
          reject = !trimbowatcavity(newpt, NULL, n, &sublist, &subceillist,
                                    tetlists, ceillists, -1.0);
        }
        if (!reject) {
          // CBC(p) should include s, so that s can be removed after CBC(p)
          //   is remeshed. However, if there are locally non-Delaunay faces
          //   and encroached subsegments, s may not be collected in CBC(p).
          //   p should not be inserted in such case.
          reject = !sinfected(encloop->ss);
        }
        if (!reject) {
          // Save a point for size interpolation.
          e1 = sorg(splitsub);
          bowatinsertsite(newpt, NULL, n, &sublist, &subceillist, tetlists,
                          ceillists, NULL, NULL, true, true, chkbadtet);
          setnewpointsize(newpt, e1, NULL);
          if (steinerleft > 0) steinerleft--;
        } else {
          // p is rejected for the one of the following reasons:
          //   (1) BC(p) is not valid.
          //   (2) s does not in CBC(p).
          //   (3) p encroaches upon some segments (queued); or
          //   (4) p is rejected by point accepting rule, or
          //   (5) due to the rejection of symp (the PBC).
          pointdealloc(newpt);
        } // if (!reject)
        // Release the cavity and free the memory.
        releasebowatcavity(NULL,n,&sublist,&subceillist,tetlists,ceillists);
        if (reject) {
          // Are there queued encroached subsegments.
          if (badsubsegs->items > 0) {
            // Repair enc-subsegments.
            oldptnum = points->items;
            repairencsegs(true, chkbadtet);
            /*if (points->items > oldptnum) {
              // Some enc-subsegments got split. Try to repair f later.
              splitsub = encloop->ss;
              if (!isdead(&splitsub)) {
                if (!shell2badface(splitsub)) {
                  checksub4encroach(&splitsub, NULL, true);
                }
              }
            }*/
          }
        }
      } else {
        // Don't insert p for one of the following reasons:
        //   (1) Locate on an existing vertex; or
        //   (2) locate outside the domain.
        // Case (1) should not be possible. If such vertex v exists, it is
        //   the circumcenter of f, ie., f is non-Delaunay. Either f was got
        //   split before by v, but survived after v was inserted, or the
        //   same for a f' which is nearly co-circular with f.  Whatsoever,
        //   there are encroached segs by v, but the routine tallencsegs()
        //   did not find them out.
        if (loc == ONVERTEX) {
          //printf("Internal error in repairencsubs():\n");
          //printf("  During repairing encroached subface (%d, %d, %d)\n",
                 //pointmark(encloop->forg), pointmark(encloop->fdest),
                 //pointmark(encloop->fapex));
          //printf("  New point %d is coincident with an existing vertex %d\n",
                 //pointmark(newpt), pointmark(sorg(splitsub)));
          terminatetetgen(2);
        }
        //assert(loc == OUTSIDE);
        // The circumcenter lies outside of the facet. Mark it to avoid
        //   rechecking it later.
        smarktest(encloop->ss);
        // Case (2) can happen when thers is a segment s which is close to f
        //   and is non-conforming Delaunay. The circumcenter of f encroaches
        //   upon s, but the circumcenter of s is rejected for insertion.
        pointdealloc(newpt);
      } // if ((loc != ONVERTEX) && (loc != OUTSIDE))
    } /*else {
      if (!isdead(&splitsub)) {
        // The subface has been changed, re-check it.
        checksub4encroach(&splitsub, NULL, true);
      }
    } // if (!isdead(&splitsub) && (sorg(splitsub) == encloop->forg) &&
    */
    // Remove this entry from list.
    badfacedealloc(badsubfaces, encloop);
  } // while ((badsubfaces->items > 0) && (steinerleft != 0))

  delete verlist;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// repairbadtets()    Repair all bad-quality tetrahedra.                     //
//                                                                           //
// All bad-quality tets are stored in pool 'badtetrahedrons'.  Each bad tet  //
// is repaired by inserting a point at or near its circumcenter. However, if //
// this point encroaches any subsegment or subface, it is not inserted. Ins- //
// tead the encroached segment and subface are split.  Newly inserted points //
// may create other bad-quality tets, these are also repaired.               //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::repairbadtets()
{
  list *tetlist, *ceillist;
  list *verlist;
  arraypool *histtetarray;
  badface *badtet;
  triface starttet;
  point newpt, e1;
  enum locateresult loc;
  bool reject;
  long oldptnum;
  int i;

  tetlist = new list(sizeof(triface), NULL, 1024);
  ceillist = new list(sizeof(triface), NULL, 1024);
  verlist = new list(sizeof(point *), NULL, 256);

  histtetarray = new arraypool(sizeof(triface), 8);

  // Loop until pool 'badtetrahedrons' is empty. Note that steinerleft == -1
  //   if an unlimited number of Steiner points is allowed.
  while ((badtetrahedrons->items > 0) && (steinerleft != 0)) {
    // Get a bad-quality tet t.
    badtet = topbadtetra();
    // Make sure that the tet is still the same one when it was tested.
    //   Subsequent transformations may have made it a different tet.
    if ((badtet != (badface *) NULL) && !isdead(&badtet->tt)
         && org(badtet->tt) == badtet->forg
         && dest(badtet->tt) == badtet->fdest
         && apex(badtet->tt) == badtet->fapex
         && oppo(badtet->tt) == badtet->foppo) {
      if (b->verbose > 1) {
        //printf("    Dequeuing btet (%d, %d, %d, %d).\n",
               //pointmark(badtet->forg), pointmark(badtet->fdest),
               //pointmark(badtet->fapex), pointmark(badtet->foppo));
      }
      // Create the new point p (at the circumcenter of t).
      makepoint(&newpt);
      for (i = 0; i < 3; i++) newpt[i] = badtet->cent[i];
      setpointtype(newpt, FREEVOLVERTEX);
      // Locate p.
      starttet = badtet->tt;
      //loc = preciselocate(newpt, &starttet, tetrahedrons->items);
      loc = locate2(newpt, &starttet, histtetarray);
      if (b->verbose > 1) {
        //printf("    loc = %d.\n", (int) loc);
      }
      if ((loc != ONVERTEX) && (loc != OUTSIDE)) {
        // For BC(p) and B(p).
        infect(starttet);
        tetlist->append(&starttet);
        formbowatcavityquad(newpt, tetlist, ceillist);
        // Check for encroached subsegments.
        reject = tallencsegs(newpt, 1, &ceillist);
        if (!reject) {
          // Check for encroached subfaces.
          reject = tallencsubs(newpt, 1, &ceillist);
        }
        // Execute point accepting rule if p does not encroach upon any
        //   subsegment and subface.
        if (!reject) {
          reject = !acceptvolpt(newpt, ceillist, verlist);
        }
        if (!reject) {
          reject = !trimbowatcavity(newpt, NULL, 1, NULL, NULL, &tetlist,
                                    &ceillist, -1.0);
        }
        if (!reject) {
          // BC(p) should include t, so that t can be removed after BC(p) is
          //   remeshed. However, if there are locally non-Delaunay faces
          //   and encroached subsegments/subfaces, t may not be collected
          //   in BC(p). p should not be inserted in such case.
          reject = !infected(badtet->tt);
          if (reject) outbowatcircumcount++;
        }
        if (!reject) {
          // Save a point for size interpolation.
          e1 = org(starttet);
          // Insert p.
          bowatinsertsite(newpt, NULL, 1, NULL, NULL, &tetlist, &ceillist,
                          NULL, NULL, false, false, true);
          setnewpointsize(newpt, e1, NULL);
          if (steinerleft > 0) steinerleft--;
        } else {
          // p is rejected for one of the following reasons:
          //   (1) BC(p) is not valid.
          //   (2) t does not in BC(p).
          //   (3) p encroaches upon some segments;
          //   (4) p encroaches upon some subfaces;
          //   (5) p is rejected by the point accepting rule.
          pointdealloc(newpt);
          // Uninfect tets of BC(p).
          for (i = 0; i < tetlist->len(); i++) {
            starttet = * (triface *)(* tetlist)[i];
            uninfect(starttet);
          }
        }
        tetlist->clear();
        ceillist->clear();
        // Split encroached subsegments/subfaces if there are.
        if (reject) {
          oldptnum = points->items;
          if (badsubsegs->items > 0) {
            repairencsegs(true, true);
          }
          if (badsubfaces->items > 0) {
            repairencsubs(true);
          }
          if (points->items > oldptnum) {
            // Some encroaching subsegments/subfaces got split. Re-queue the
            //   tet if it is still alive.
            starttet = badtet->tt;
            if (!isdead(&starttet)) {
              checktet4badqual(&starttet, true);
            }
          }
        }
      } else {
        // Do not insert p. The reason may be one of:
        //   (1) p is coincident (ONVERTEX) with an existing vertex; or
        //   (2) p is outside (OUTSIDE) the mesh.
        // Case (1) should not be possible. If such vertex v exists, it is
        //   the circumcenter of t, ie., t is non-Delaunay. Either t was got
        //   split before by v, but survived after v was inserted, or the
        //   same for a t' which is nearly co-spherical with t.  Whatsoever,
        //   there are encroached segments or subfaces by v but the routines
        //   tallencsegs() or tallencsubs() did not find them out.
        /*if (loc == ONVERTEX) {
          //printf("Internal error in repairbadtets():\n");
          //printf("  During repairing bad tet (%d, %d, %d, %d)\n",
                 pointmark(badtet->forg), pointmark(badtet->fdest),
                 pointmark(badtet->fapex), pointmark(badtet->foppo));
          //printf("  New point %d is coincident with an existing vertex %d\n",
                 pointmark(newpt), pointmark(org(starttet)));
          terminatetetgen(2);
        }*/
        // Case (2) can happen when there is a segment s (or subface f) which
        //   is close to f and is non-conforming Delaunay.  The circumcenter
        //   of t encroaches upon s (or f), but the circumcenter of s (or f)
        //   is rejected for insertion.
        pointdealloc(newpt);
      } // if ((loc != ONVERTEX) && (loc != OUTSIDE))
    } // if (!isdead(&badtet->tt) && org(badtet->tt) == badtet->forg &&
    // Remove the tet from the queue.
    dequeuebadtet();
  } // while ((badtetrahedrons->items > 0) && (steinerleft != 0))

  delete tetlist;
  delete ceillist;
  delete verlist;
  delete histtetarray;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// enforcequality()    Refine the mesh.                                      //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::enforcequality()
{
  long total, vertcount;
  int i;
  
  if (!b->quiet) {
    //printf("Adding Steiner points to enforce quality.\n");
  } 

  total = vertcount = 0l;
  if (b->conformdel) {
    r2count = r3count = 0l;
  }

  // If both '-D' and '-r' options are used. 
  if (b->conformdel && b->refine) {
    markacutevertices(65.0);
  }
  // If '-m' is not used.
  if (!b->metric) {
    // Find and mark all sharp segments.
    marksharpsegments(65.0);
    // Decide the sizes for feature points.
    decidefeaturepointsizes();
  }

  // Initialize the pool of encroached subsegments.
  badsubsegs = new memorypool(sizeof(badface), SUBPERBLOCK, POINTER, 0);
  // Looking for encroached subsegments.
  tallencsegs(NULL, 0, NULL);
  if (b->verbose && badsubsegs->items > 0) {
    //printf("  Splitting encroached subsegments.\n");
  }
  vertcount = points->items;
  // Fix encroached segments without noting any enc subfaces.
  repairencsegs(false, false);
  if (b->verbose > 0) {
    //printf("  %ld split points.\n", points->items - vertcount);
  }
  total += points->items - vertcount;

  // Initialize the pool of encroached subfaces.
  badsubfaces = new memorypool(sizeof(badface), SUBPERBLOCK, POINTER, 0);
  // Initialize the priority queues of badfaces.
  for (i = 0; i < 3; i++) subquefront[i] = (badface *) NULL;
  for (i = 0; i < 3; i++) subquetail[i] = &subquefront[i];
  // Looking for encroached subfaces.
  tallencsubs(NULL, 0, NULL);
  if (b->verbose && badsubfaces->items > 0) {
    //printf("  Splitting encroached subfaces.\n");
  }
  vertcount = points->items;
  // Fix encroached subfaces without noting bad tetrahedra.
  repairencsubs(false);
  if (b->verbose > 0) {
    //printf("  %ld split points.\n", points->items - vertcount);
  }
  total += points->items - vertcount;
  // At this point, the mesh should be conforming Delaunay if no input
  //   angle is smaller than 90 degree.

  // Next, fix bad quality tetrahedra.
  if ((b->minratio > 0.0) || b->varvolume || b->fixedvolume) {
    // Initialize the pool of bad tets
    badtetrahedrons = new memorypool(sizeof(badface), ELEPERBLOCK, POINTER, 0);
    // Initialize the priority queues of bad tets.
    for (i = 0; i < 64; i++) tetquefront[i] = (badface *) NULL;
    firstnonemptyq = -1;
    recentq = -1;
    // Looking for bad quality tets.
    cosmaxdihed = cos(b->maxdihedral * PI / 180.0);
    cosmindihed = cos(b->mindihedral * PI / 180.0);
    tallbadtetrahedrons();
    if (b->verbose && badtetrahedrons->items > 0) {
      //printf("  Splitting bad tetrahedra.\n");
    }
    vertcount = points->items;
    repairbadtets();
    if (b->verbose > 0) {
      //printf("  %ld refinement points.\n", points->items - vertcount);
    }
    total += points->items - vertcount;
    delete badtetrahedrons;
  }

  if (b->verbose > 0) {
    //printf("  Totally added %ld points.\n", total);
  }

  delete badsubfaces;
  delete badsubsegs;
}

////                                                                       ////
////                                                                       ////
//// refine_cxx ///////////////////////////////////////////////////////////////

//// optimize_cxx /////////////////////////////////////////////////////////////
////                                                                       ////
////                                                                       ////

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// checktet4ill()    Check a tet to see if it is illegal.                    //
//                                                                           //
// A tet is "illegal" if it spans on one input facet.  Save the tet in queue //
// if it is illegal and the flag 'enqflag' is set.                           //
//                                                                           //
// Note: Such case can happen when the input facet has non-coplanar vertices //
// and the Delaunay tetrahedralization of the vertices may creat such tets.  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::checktet4ill(triface* testtet, bool enqflag)
{
  badface *newbadtet;
  triface checktet;
  face checksh1, checksh2;
  face checkseg;
  bool illflag;
  int i;

  illflag = false;
  for (testtet->loc = 0; testtet->loc < 4; testtet->loc++) {
    tspivot(*testtet, checksh1);
    if (checksh1.sh != dummysh) {
      testtet->ver = 0;
      findedge(&checksh1, org(*testtet), dest(*testtet));
      for (i = 0; i < 3; i++) {
        fnext(*testtet, checktet);
        tspivot(checktet, checksh2);
        if (checksh2.sh != dummysh) {
          // Two subfaces share this edge.
          sspivot(checksh1, checkseg);
          if (checkseg.sh == dummysh) {
            // The four corners of the tet are on one facet. Illegal! Try to
            //   flip the opposite edge of the current one.
            enextfnextself(*testtet);
            enextself(*testtet);
            illflag = true; 
            break;
          }
        }
        enextself(*testtet);
        senextself(checksh1);
      }
    }
    if (illflag) break;
  }

  if (illflag && enqflag) {
    // Allocate space for the bad tetrahedron.
    newbadtet = (badface *) badtetrahedrons->alloc();
    newbadtet->tt = *testtet;
    newbadtet->key = -1.0; // = 180 degree.
    for (i = 0; i < 3; i++) newbadtet->cent[i] = 0.0;
    newbadtet->forg = org(*testtet);
    newbadtet->fdest = dest(*testtet);
    newbadtet->fapex = apex(*testtet);
    newbadtet->foppo = oppo(*testtet);
    newbadtet->nextitem = (badface *) NULL;
    if (b->verbose > 2) {
      //printf("    Queueing illtet: (%d, %d, %d, %d).\n",
             //pointmark(newbadtet->forg), pointmark(newbadtet->fdest),
             //pointmark(newbadtet->fapex), pointmark(newbadtet->foppo));
    }
  }

  return illflag;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// checktet4opt()    Check a tet to see if it needs to be optimized.         //
//                                                                           //
// A tet t needs to be optimized if it fails to certain quality measures.    //
// The only quality measure currently used is the maximal dihedral angle at  //
// edges. The desired maximal dihedral angle is 'b->maxdihedal' (set by the  //
// '-qqq' option.                                                            //
//                                                                           //
// A tet may have one, two, or three big dihedral angles. Examples: Let the  //
// tet t = abcd, and its four corners are nearly co-planar. Then t has one   //
// big dihedral angle if d is very close to the edge ab; t has three big     //
// dihedral angles if d's projection on the face abc is also inside abc, i.e.//
// the shape of t likes a hat; finally, t has two big dihedral angles if d's //
// projection onto abc is outside abc.                                       //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::checktet4opt(triface* testtet, bool enqflag)
{
  badface *newbadtet;
  point pa, pb, pc, pd;
  REAL N[4][3], len;
  REAL cosd;
  int count;
  int i, j;

  pa = (point) testtet->tet[4];
  pb = (point) testtet->tet[5];
  pc = (point) testtet->tet[6];
  pd = (point) testtet->tet[7];
  // Compute the 4 face normals: N[0] cbd, N[1] acd, N[2] bad, N[3] abc.
  tetallnormal(pa, pb, pc, pd, N, NULL);
  // Normalize the normals.
  for (i = 0; i < 4; i++) {
    len = sqrt(dot(N[i], N[i]));
    if (len != 0.0) {
      for (j = 0; j < 3; j++) N[i][j] /= len;
    }
  }

  count = 0;

  // Find all large dihedral angles.  
  for (i = 0; i < 6; i++) {
    // Locate the edge i and calculate the dihedral angle at the edge.
    testtet->loc = 0;
    testtet->ver = 0;
    switch (i) {
    case 0: // edge ab
      cosd = -dot(N[2], N[3]);
      break;
    case 1: // edge cd 
      enextfnextself(*testtet);
      enextself(*testtet);
      cosd = -dot(N[0], N[1]);
      break;
    case 2: // edge bd
      enextfnextself(*testtet);
      enext2self(*testtet);
      cosd = -dot(N[0], N[2]);
      break;
    case 3: // edge bc
      enextself(*testtet);
      cosd = -dot(N[0], N[3]);
      break;
    case 4: // edge ad
      enext2fnextself(*testtet);
      enextself(*testtet);
      cosd = -dot(N[1], N[2]);
      break;
    case 5: // edge ac
      enext2self(*testtet);
      cosd = -dot(N[1], N[3]);
      break;
    }
    if (cosd < cosmaxdihed) {
      // A bigger dihedral angle.
      count++;
      if (enqflag) {
        // Allocate space for the bad tetrahedron.
        newbadtet = (badface *) badtetrahedrons->alloc();
        newbadtet->tt = *testtet;
        newbadtet->key = cosd;
        for (j = 0; j < 3; j++) newbadtet->cent[j] = 0.0;
        newbadtet->forg = org(*testtet);
        newbadtet->fdest = dest(*testtet);
        newbadtet->fapex = apex(*testtet);
        newbadtet->foppo = oppo(*testtet);
        newbadtet->nextitem = (badface *) NULL;
        if (b->verbose > 2) {
          //printf("    Queueing tet: (%d, %d, %d, %d), dihed %g (degree).\n",
                 //pointmark(newbadtet->forg), pointmark(newbadtet->fdest),
                 //pointmark(newbadtet->fapex), pointmark(newbadtet->foppo),
                 //acos(cosd) * 180.0 / PI);
        }
      }
    }
  }

  return count > 0;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// removeedge()    Remove an edge                                            //
//                                                                           //
// 'remedge' is a tet (abcd) having the edge ab wanted to be removed.  Local //
// reconnecting operations are used to remove edge ab.  The following opera- //
// tion will be tryed.                                                       //
//                                                                           //
// If ab is on the hull, and abc and abd are both hull faces. Then ab can be //
// removed by stripping abcd from the mesh. However, if ab is a segemnt, do  //
// the operation only if 'b->optlevel' > 1 and 'b->nobisect == 0'.           //
//                                                                           //
// If ab is an internal edge, there are n tets contains it.  Then ab can be  //
// removed if there exists another m tets which can replace the n tets with- //
// out changing the boundary of the n tets.                                  //
//                                                                           //
// If 'optflag' is set.  The value 'remedge->key' means cos(theta), where    //
// 'theta' is the maximal dishedral angle at ab. In this case, even if the   //
// n-to-m flip exists, it will not be performed if the maximum dihedral of   //
// the new tets is larger than 'theta'.                                      //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::removeedge(badface* remedge, bool optflag)
{
  triface abcd, badc;  // Tet configuration at edge ab.
  triface baccasing, abdcasing;
  triface abtetlist[21];  // Old configuration at ab, save maximum 20 tets.
  triface bftetlist[21];  // Old configuration at bf, save maximum 20 tets.
  triface newtetlist[90]; // New configuration after removing ab.
  face checksh;
  //enum fliptype fty;
  REAL key;
  bool remflag, subflag;
  int n, n1, m, i, j, k;

  triface newtet;
  point *ppt;

  // First try to strip abcd from the mesh. This needs to check either ab
  //   or cd is on the hull. Try to strip it whichever is true.
  abcd = remedge->tt;
  adjustedgering(abcd, CCW);
  k = 0;
  do {
    sym(abcd, baccasing);
    // Is the tet on the hull?
    if (baccasing.tet == dummytet) {
      fnext(abcd, badc);
      sym(badc, abdcasing);
      if (abdcasing.tet == dummytet) {
        // Strip the tet from the mesh -> ab is removed as well.
        if (removetetbypeeloff(&abcd, newtetlist)) {
          if (b->verbose > 1) {
            //printf("    Stripped tet from the mesh.\n");
          }
          optcount[0]++;
          opt_tet_peels++;
          // edge is removed. Test new tets for further optimization.
          for (i = 0; i < 2; i++) {
            if (optflag) {
              checktet4opt(&(newtetlist[i]), true);
            } else {
              checktet4ill(&(newtetlist[i]), true);
            }
          }
          // Update the point-to-tet map
          for (i = 0; i < 2; i++) {
            newtet = newtetlist[i];
            ppt = (point *) &(newtet.tet[4]);
            for (j = 0; j < 4; j++) {
              setpoint2tet(ppt[j], encode(newtet));
            }
          }
          return true;
        }
      }
    }
    // Check if the oppsite edge cd is on the hull.
    enext2fnextself(abcd);
    enext2self(abcd);
    esymself(abcd); // --> cdab
    k++;
  } while (k < 2);

  // Get the tets configuration at ab. Collect maximum 10 tets.
  subflag = false;
  abcd = remedge->tt;
  adjustedgering(abcd, CW);
  n = 0;
  abtetlist[n] = abcd;
  do {
    // Is the list full?
    if (n == 20) break;
    // Stop if a subface appears.
    tspivot(abtetlist[n], checksh);
    if (checksh.sh != dummysh) {
      // ab is either a segment or a facet edge. The latter case is not
      //   handled yet! An edge flip is needed.
      subflag = true; break; // return false;
    }
    // Get the next tet at ab.
    fnext(abtetlist[n], abtetlist[n + 1]);
    n++;
  } while (apex(abtetlist[n]) != apex(abcd));

  remflag = false;
  key = remedge->key;

  if (subflag && optflag) {
    /*abcd = remedge->tt;
    adjustedgering(abcd, CCW);
    // Try to flip face cda or cdb to improve quality.
    for (j = 0; j < 2; j++) {
      if (j == 0) {
        enext2fnext(abcd, abtetlist[0]); // Goto cda.
      } else {
        enextfnext(abcd, abtetlist[0]); // Goto cdb.
      }
      fty = categorizeface(abtetlist[0]);
      if (fty == T23) {
        // A 2-to-3 flip is possible.
        sym(abtetlist[0], abtetlist[1]);
        //assert(abtetlist[1].tet != dummytet);
        n = 2; 
        m = 3;
        remflag = removefacebyflip23(&key, abtetlist, newtetlist, NULL);
      } else if (fty == T22) {
        // A 2-to-2 or 4-to-4 flip is possible.
        n = 2;
        newtetlist[0] = abtetlist[0];
        adjustedgering(newtetlist[0], CW);
        fnext(newtetlist[0], newtetlist[1]);
        //assert(newtetlist[1].tet != dummytet);
        // May it is 4-to-4 flip.
        if (fnext(newtetlist[1], newtetlist[2])) {
          fnext(newtetlist[2], newtetlist[3]);
          //assert(newtetlist[3].tet != dummytet);
          n = 4;
        }
        m = n;
        remflag = removeedgebyflip22(&key, n, newtetlist, NULL);
      }
      // Has quality been improved?
      if (remflag) {
        if (b->verbose > 1) {
          //printf("  Done flip %d-to-%d. Qual: %g -> %g.\n", n, m,
                 acos(remedge->key) / PI * 180.0, acos(key) / PI * 180.0);
        }
        // Delete the old tets. Note, flip22() does not create new tets.
        if (m == 3) {
          for (i = 0; i < n; i++) {
            tetrahedrondealloc(abtetlist[i].tet);
          }
        }
        for (i = 0; i < m; i++) {
          checktet4opt(&(newtetlist[i]), true);
        }
        // Update the point-to-tet map
        for (i = 0; i < m; i++) {
          newtet = newtetlist[i];
          ppt = (point *) &(newtet.tet[4]);
          for (j = 0; j < 4; j++) {
            setpoint2tet(ppt[j], encode(newtet));
          }
        }
        optcount[1]++;
        opt_face_flips++;
        return true;
      }
    } // j
    */
    // Faces are not flipable. Return.
    return false;
  }

  // 2 < n < 20.
  if (n == 3) {
    // There are three tets at ab. Try to do a flip32 at ab.
    remflag = removeedgebyflip32(&key, abtetlist, newtetlist, NULL);
  } else if ((n > 3) && (n <= b->maxflipedgelinksize)) {
    // Four tets case. Try to do edge transformation.
    remflag = removeedgebytranNM(&key,n,abtetlist,newtetlist,NULL,NULL,NULL);
  } else {
    if (b->verbose > 1) {
      //printf("  !! Unhandled case: n = %d.\n", n);
    }
  }
  if (remflag) {
    optcount[n]++;
    // Delete the old tets.
    for (i = 0; i < n; i++) {
      tetrahedrondealloc(abtetlist[i].tet);
    }
    m = (n - 2) * 2; // The numebr of new tets.
    if (b->verbose > 1) {
      //printf("  Done flip %d-to-%d. ", n, m);
      if (optflag) {
        //printf("Qual: %g -> %g.", acos(remedge->key) / PI * 180.0,
              // acos(key) / PI * 180.0);
      }
      //printf("\n");
    }
  }

  if (!remflag && (key == remedge->key) && (n <= b->maxflipedgelinksize)) {
    // Try to do a combination of flips.
    n1 = 0;
    remflag = removeedgebycombNM(&key, n, abtetlist, &n1, bftetlist,
      newtetlist, NULL);
    if (remflag) {
      optcount[9]++;
      // Delete the old tets.
      for (i = 0; i < n; i++) {
        tetrahedrondealloc(abtetlist[i].tet);
      }
      for (i = 0; i < n1; i++) {
        if (!isdead(&(bftetlist[i]))) {
          tetrahedrondealloc(bftetlist[i].tet);
        }
      }
      m = ((n1 - 2) * 2 - 1) + (n - 3) * 2; // The number of new tets.
      if (b->verbose > 1) {
        //printf("  Done flip %d-to-%d (n-1=%d, n1=%d). ", n+n1-2, m, n-1,n1);
        if (optflag) {
          //printf("Qual: %g -> %g.", acos(remedge->key) / PI * 180.0,
               //acos(key) / PI * 180.0);
        }
        //printf("\n");
      }
    }
  }

  if (remflag) {
    // edge is removed. Test new tets for further optimization.
    for (i = 0; i < m; i++) {
      if (optflag) {
        checktet4opt(&(newtetlist[i]), true);
      } else {
        checktet4ill(&(newtetlist[i]), true);
      }
    }
    // Update the point-to-tet map
    for (i = 0; i < m; i++) {
      newtet = newtetlist[i];
      ppt = (point *) &(newtet.tet[4]);
      for (j = 0; j < 4; j++) {
        setpoint2tet(ppt[j], encode(newtet));
      }
    }
    opt_edge_flips++;
  }

  return remflag;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// smoothpoint()    Smooth a volume/segment point.                           //
//                                                                           //
// 'smthpt' (p) is inside the polyhedron (C) bounded by faces in 'starlist'. //
// This routine moves p inside C until an object function is maximized.      //
//                                                                           //
// Default, the CCW edge ring of the faces on C points to p. If 'invtori' is //
// TRUE, the orientation is inversed.                                        //
//                                                                           //
// If 'key' != NULL, it contains an object value to be improved. Current it  //
// means the cosine of the largest dihedral angle. In such case, the point   //
// is smoothed only if the final configuration improves the object value, it //
// is returned by the 'key'.                                                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::smoothpoint(point smthpt, point e1, point e2, list *starlist,
  bool invtori, REAL *key)
{
  triface starttet;
  point pa, pb, pc;
  REAL fcent[3], startpt[3], nextpt[3], bestpt[3];
  REAL iniTmax, oldTmax, newTmax;
  REAL ori, aspT, aspTmax, imprate;
  REAL cosd, maxcosd;
  bool segflag, randflag; //, subflag; 
  int numdirs;
  int iter, i, j;

  // Is p a segment vertex?
  segflag = (e1 != (point) NULL);
  // Decide the number of moving directions.
  numdirs = segflag ? 2 : starlist->len();
  randflag = numdirs > 10;
  if (randflag) {
    numdirs = 10; // Maximum 10 directions.
  }

  // Calculate the initial object value (the largest aspect ratio).
  for (i = 0; i < starlist->len(); i++) {
    starttet = * (triface *)(* starlist)[i];
    adjustedgering(starttet, !invtori ? CCW : CW);
    pa = org(starttet);
    pb = dest(starttet);
    pc = apex(starttet);
    aspT = tetaspectratio(pa, pb, pc, smthpt);
    if (i == 0) {
      aspTmax = aspT;
    } else {
      aspTmax = aspT > aspTmax ? aspT : aspTmax;
    }
  }
  iniTmax = aspTmax;

  if (b->verbose > 1) {
    //printf("    Smooth %s point %d (%g, %g, %g).\n", segflag ? "seg" : "vol",
           //pointmark(smthpt), smthpt[0], smthpt[1], smthpt[2]);
    //printf("    Initial max L/h = %g.\n", iniTmax);
  }
  for (i = 0; i < 3; i++) {
    bestpt[i] = startpt[i] = smthpt[i];
  }

  // Do iteration until the new aspTmax does not decrease.
  newTmax = iniTmax;
  iter = 0;
  while (true) {
    // Find the best next location.
    oldTmax = newTmax;
    for (i = 0; i < numdirs; i++) {
      // Calculate the moved point (saved in 'nextpt').
      if (!segflag) {
        if (randflag) {
          // Randomly pick a direction.
          j = (int) randomnation(starlist->len());
        } else {
          j = i;
        }
        starttet = * (triface *)(* starlist)[j];
        adjustedgering(starttet, !invtori ? CCW : CW);
        pa = org(starttet);
        pb = dest(starttet);
        pc = apex(starttet);
        for (j = 0; j < 3; j++) {
          fcent[j] = (pa[j] + pb[j] + pc[j]) / 3.0;
        }
      } else {
        for (j = 0; j < 3; j++) {
          fcent[j] = (i == 0 ? e1[j] : e2[j]);
        }
      }
      for (j = 0; j < 3; j++) {
        nextpt[j] = startpt[j] + 0.01 * (fcent[j] - startpt[j]); 
      }
      // Get the largest object value for the new location.
      for (j = 0; j < starlist->len(); j++) {
        starttet = * (triface *)(* starlist)[j];
        adjustedgering(starttet, !invtori ? CCW : CW);
        pa = org(starttet);
        pb = dest(starttet);
        pc = apex(starttet);
        ori = orient3d(pa, pb, pc, nextpt);
        if (ori < 0.0) {
          aspT = tetaspectratio(pa, pb, pc, nextpt);
          if (j == 0) {
            aspTmax = aspT;
          } else {
            aspTmax = aspT > aspTmax ? aspT : aspTmax;
          }
        } else {
          // An invalid new tet. Discard this point.
          aspTmax = newTmax;
        } // if (ori < 0.0)
        // Stop looping when the object value is bigger than before.
        if (aspTmax >= newTmax) break;
      } // for (j = 0; j < starlist->len(); j++)
      if (aspTmax < newTmax) {
        // Save the improved object value and the location.
        newTmax = aspTmax;
        for (j = 0; j < 3; j++) bestpt[j] = nextpt[j];
      }
    } // for (i = 0; i < starlist->len(); i++)
    // Does the object value improved much?
    imprate = fabs(oldTmax - newTmax) / oldTmax;
    if (imprate < 1e-3) break;
    // Yes, move p to the new location and continue.
    for (j = 0; j < 3; j++) startpt[j] = bestpt[j];
    iter++;
  } // while (true)

  if (iter > 0) {
    // The point is moved.
    if (key) {
      // Check if the quality is improved by the smoothed point.
      maxcosd = 0.0; // = cos(90).
      for (j = 0; j < starlist->len(); j++) {
        starttet = * (triface *)(* starlist)[j];
        adjustedgering(starttet, !invtori ? CCW : CW);
        pa = org(starttet);
        pb = dest(starttet);
        pc = apex(starttet);
        tetalldihedral(pa, pb, pc, startpt, NULL, &cosd, NULL);
        if (cosd < *key) {
          // This quality will not be improved. Stop.
          iter = 0; break;
        } else {
          // Remeber the worst quality value (of the new configuration).
          maxcosd = maxcosd < cosd ? maxcosd : cosd;
        }
      }
      if (iter > 0) *key = maxcosd;
    }
  }

  if (iter > 0) {
    if (segflag) smoothsegverts++;
    for (i = 0; i < 3; i++) smthpt[i] = startpt[i];
    if (b->verbose > 1) {
      //printf("    Move to new location (%g, %g, %g).\n", smthpt[0], smthpt[1],
             //smthpt[2]);
      //printf("    Final max L/h = %g. (%d iterations)\n", newTmax, iter);
      if (key) {
        //printf("    Max. dihed = %g (degree).\n", acos(*key) / PI * 180.0);
      }
    }
    return true;
  } else {
    if (b->verbose > 1) {
      //printf("    Not smoothed.\n");
    }
    return false;
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// smoothsliver()    Remove a sliver by smoothing a vertex of it.            //
//                                                                           //
// The 'slivtet' represents a sliver abcd, and ab is the current edge which  //
// has a large dihedral angle (close to 180 degree).                         //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::smoothsliver(badface* remedge, list *starlist)
{
  triface checktet;
  point smthpt;
  bool smthed;
  int idx, i, j;

  // Find a Steiner volume point and smooth it.
  smthed = false;
  for (i = 0; i < 4 && !smthed; i++) {
    smthpt = (point) remedge->tt.tet[4 + i];
    // Is it a volume point?
    if (pointtype(smthpt) == FREEVOLVERTEX) {
      // Is it a Steiner point?
      idx = pointmark(smthpt) - in->firstnumber;
      if (!(idx < in->numberofpoints)) {
        // Smooth a Steiner volume point.
        starlist->append(&(remedge->tt.tet));
        formstarpolyhedron(smthpt, starlist, NULL, false);
        smthed = smoothpoint(smthpt,NULL,NULL,starlist,false,&remedge->key);
        // If it is smoothed. Queue new bad tets.
        if (smthed) {
          for (j = 0; j < starlist->len(); j++) {
            checktet = * (triface *)(* starlist)[j];
            checktet4opt(&checktet, true);
          }
        }
        starlist->clear();
      }
    }
  } 

  /* Omit to smooth segment points. This may cause infinite loop.
  if (smthed) {
    return true;
  }
  face abseg, nextseg, prevseg;
  point pt[2];
  // Check if ab is a segment.
  tsspivot(slivtet, &abseg);
  if (abseg.sh == dummysh) {
    // ab is not a segment. Check if a or b is a Steiner segment point.
    for (i = 0; i < 2 && !smthed; i++) {
      smthpt = (i == 0 ? org(*slivtet) : dest(*slivtet));
      if (pointtype(smthpt) == FREESEGVERTEX) {
        // Is it a Steiner point?
        idx = pointmark(smthpt) - in->firstnumber;
        if (!(idx < in->numberofpoints)) {
          // Smooth a Steiner segment point. Get the segment.
          sdecode(point2sh(smthpt), nextseg);
          locateseg(smthpt, &nextseg);
          //assert(sorg(nextseg) == smthpt);
          pt[0] = sdest(nextseg);
          senext2(nextseg, prevseg);
          spivotself(prevseg);
          prevseg.shver = 0;
          if (sorg(prevseg) == smthpt) sesymself(prevseg);
          //assert(sdest(prevseg) == smthpt);
          pt[1] = sorg(prevseg);
          starlist->append(slivtet);
          formstarpolyhedron(smthpt, starlist, NULL, true);
          smthed = smoothpoint(smthpt, pt[0], pt[1], starlist, false);
          // If it is smoothed. Check if the tet is still a sliver.
          if (smthed) checktet4opt(slivtet, true);
          starlist->clear();
        }
      }
    }
  }
  */

  return smthed;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// splitsliver()    Remove a sliver by inserting a point.                    //
//                                                                           //
// The 'remedge->tt' represents a sliver abcd, ab is the current edge which  //
// has a large dihedral angle (close to 180 degree).                         //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

bool tetgenmesh::splitsliver(badface *remedge, list *tetlist, list *ceillist)
{
  triface starttet;
  face checkseg;
  point newpt, pt[4];
  bool remflag;
  int i;

  // Let 'remedge->tt' be the edge [a, b].
  starttet = remedge->tt;

  // Go to the opposite edge [c, d].
  adjustedgering(starttet, CCW);
  enextfnextself(starttet);
  enextself(starttet);

  // Check if cd is a segment.
  tsspivot(&starttet, &checkseg);
  if (b->nobisect == 0) {
    if (checkseg.sh != dummysh) {
      // cd is a segment. The seg will be split.
      checkseg.shver = 0;
      pt[0] = sorg(checkseg);
      pt[1] = sdest(checkseg);
      makepoint(&newpt);
      getsplitpoint(pt[0], pt[1], NULL, newpt);
      setpointtype(newpt, FREESEGVERTEX);
      setpoint2seg(newpt, sencode(checkseg));
      // Insert p, this should always success.
      sstpivot(&checkseg, &starttet);
      splittetedge(newpt, &starttet, NULL);
      // Collect the new tets connecting at p.
      sstpivot(&checkseg, &starttet);
      ceillist->append(&starttet);
      formstarpolyhedron(newpt, ceillist, NULL, true);
      setnewpointsize(newpt, pt[0], NULL);
      if (steinerleft > 0) steinerleft--;
      // Smooth p.
      smoothpoint(newpt, pt[0], pt[1], ceillist, false, NULL);
      // Queue new slivers.
      for (i = 0; i < ceillist->len(); i++) {
        starttet = * (triface *)(* ceillist)[i];
        checktet4opt(&starttet, true);
      }
      ceillist->clear();
      return true;
    }
  }

  // Create the new point p (at the circumcenter of t).
  makepoint(&newpt);
  /*// Get the four corners.
  for (i = 0; i < 4; i++) {
    pt[i] = (point) starttet.tet[4 + i];
  }
  for (i = 0; i < 3; i++) {
    newpt[i] = 0.25 * (pt[0][i] + pt[1][i] + pt[2][i] + pt[3][i]);
  }*/
  pt[0] = org(starttet);
  pt[1] = dest(starttet);
  for (i = 0; i < 3; i++) {
    newpt[i] = 0.5 * (pt[0][i] + pt[1][i]);
  }
  setpointtype(newpt, FREEVOLVERTEX);

  // Form the Bowyer-Watson cavity of p.
  remflag = false;
  infect(starttet);
  tetlist->append(&starttet);
  formbowatcavityquad(newpt, tetlist, ceillist);
  if (trimbowatcavity(newpt, NULL, 1, NULL, NULL, &tetlist, &ceillist, -1.0)) {
    // Smooth p.
    if (smoothpoint( newpt, NULL, NULL, ceillist, false, &remedge->key)) {
      // Insert p.
      bowatinsertsite(newpt, NULL, 1, NULL, NULL, &tetlist, &ceillist, NULL,
                      NULL, false, false, false);
      setnewpointsize(newpt, pt[0], NULL);
      if (steinerleft > 0) steinerleft--;
      // Queue new slivers.
      for (i = 0; i < ceillist->len(); i++) {
        starttet = * (triface *)(* ceillist)[i];
        checktet4opt(&starttet, true);
      }
      remflag = true;
    } // if (smoothpoint) 
  } // if (trimbowatcavity) 

  if (!remflag) {
    // p is rejected for BC(p) is not valid.
    pointdealloc(newpt);
    // Uninfect tets of BC(p).
    for (i = 0; i < tetlist->len(); i++) {
      starttet = * (triface *)(* tetlist)[i];
      uninfect(starttet);
    }
  }
  tetlist->clear();
  ceillist->clear();

  return remflag;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// tallslivers()    Queue all the slivers in the mesh.                       //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::tallslivers(bool optflag)
{
  triface tetloop;

  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  while (tetloop.tet != (tetrahedron *) NULL) {
    if (optflag) {
      checktet4opt(&tetloop, true);
    } else {
      checktet4ill(&tetloop, true);
    }
    tetloop.tet = tetrahedrontraverse();
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// optimizemesh()    Improving the mesh quality.                             //
//                                                                           //
// Available mesh optimizing operations are: (1) multiple edge flips (3-to-2,//
// 4-to-4, 5-to-6, etc), (2) free vertex deletion, (3) new vertex insertion. //
// (1) is mandatory, while (2) and (3) are optionally.                       //
//                                                                           //
// The variable 'b->optlevel' (set after '-s') determines the use of these   //
// operations. If it is: 0, do no optimization; 1, only do (1) operation; 2, //
// do (1) and (2) operations; 3, do all operations. Deault, b->optlvel = 2.  //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::optimizemesh2(bool optflag)
{
  list *splittetlist, *tetlist, *ceillist;
  badface *remtet, *newbadtet;
  REAL maxdihed, objdihed, cosobjdihed;
  long oldflipcount, newflipcount;
  long oldpointcount;
  int slivercount;
  int optpasscount;
  int iter, i;

  // Cosines of the six dihedral angles of the tet [a, b, c, d]. 
  //   From cosdd[0] to cosdd[5]: ab, bc, ca, ad, bd, cd.
  REAL cosdd[6];
  //int j;

  if (!b->quiet) {
    if (optflag) {
      if (b_steinerflag) {
        // This routine is called from removesteiners2();
      } else {
        //printf("Optimizing mesh.\n");
      }
    } else {
      //printf("Repairing mesh.\n");
    }
  }

  if (optflag) {
    if (b_steinerflag) {
      // This routine is called from removesteiners2();
      cosmaxdihed = cos(179.0 * PI / 180.0);
      cosmindihed = cos(1.0 * PI / 180.0);
      // The radian of the maximum dihedral angle.
      maxdihed = 179.0 / 180.0 * PI;
    } else {
      cosmaxdihed = cos(b->maxdihedral * PI / 180.0);
      cosmindihed = cos(b->mindihedral * PI / 180.0);
      // The radian of the maximum dihedral angle.
      maxdihed = b->maxdihedral / 180.0 * PI;
      // A sliver has an angle large than 'objdihed' will be split.
      objdihed = b->maxdihedral + 5.0;
      if (objdihed < 175.0) objdihed = 175.0; 
      objdihed = objdihed / 180.0 * PI;
      cosobjdihed = cos(objdihed);
    }
  }

  // Initialize the pool of bad tets.
  badtetrahedrons = new memorypool(sizeof(badface), ELEPERBLOCK, POINTER, 0);
  // Looking for non-optimal tets.
  tallslivers(optflag);

  oldpointcount = points->items;
  opt_tet_peels = opt_face_flips = opt_edge_flips = 0l;
  oldflipcount = newflipcount = 0l;
  smoothsegverts = 0l;
  optpasscount = 0;

  if (optflag && (b->verbose)) {
    //printf("  level = %d.\n", b->optlevel);
  }

  // Start the mesh optimization iteration.
  do {

    if (optflag && (b->verbose > 1)) {
      //printf("  level = %d.\n", b->optlevel);
    }

    // Improve the mesh quality by flips.
    iter = 0;
    do {
      oldflipcount = newflipcount;
      // Loop in the list of bad tets.
      badtetrahedrons->traversalinit();
      remtet = badfacetraverse(badtetrahedrons);
      while (remtet != (badface *) NULL) {
        if (!isdead(&remtet->tt) && (org(remtet->tt) == remtet->forg) &&
            (dest(remtet->tt) == remtet->fdest) && 
            (apex(remtet->tt) == remtet->fapex) &&
            (oppo(remtet->tt) == remtet->foppo)) {
          if (b->verbose > 1) {
            //printf("    Repair tet (%d, %d, %d, %d) %g (degree).\n",
                   //pointmark(remtet->forg), pointmark(remtet->fdest),
                  // pointmark(remtet->fapex), pointmark(remtet->foppo),
                  // acos(remtet->key) / PI * 180.0);
          }
          if (removeedge(remtet, optflag)) {
            // Remove the badtet from the list.
            badfacedealloc(badtetrahedrons, remtet);
          }
        } else {
          // Remove the badtet from the list.
          badfacedealloc(badtetrahedrons, remtet);
        }
        remtet = badfacetraverse(badtetrahedrons);
      }
      iter++;
      if (iter > 10) break; // Stop at 10th iterations.
      // Count the total number of flips.
      newflipcount = opt_tet_peels + opt_face_flips + opt_edge_flips;
      // Continue if there are bad tets and new flips.
    } while ((badtetrahedrons->items > 0) && (newflipcount > oldflipcount));

    if (b_steinerflag) {
      // This routine was called from removesteiner2(). Do not repair
      //   the bad tets by splitting.
      badtetrahedrons->restart();
    }

    if ((badtetrahedrons->items > 0l) && optflag  && (b->optlevel > 2)) {
      // Get a list of slivers and try to split them.
      splittetlist = new list(sizeof(badface), NULL, 256);
      tetlist = new list(sizeof(triface), NULL, 256);
      ceillist = new list(sizeof(triface), NULL, 256);

      // Form a list of slivers to be split and clean the pool.
      badtetrahedrons->traversalinit();
      remtet = badfacetraverse(badtetrahedrons);
      while (remtet != (badface *) NULL) {
        splittetlist->append(remtet);
        remtet = badfacetraverse(badtetrahedrons);
      }
      // Clean the pool of bad tets.
      badtetrahedrons->restart();
      slivercount = 0;
      for (i = 0; i < splittetlist->len(); i++) {
        remtet = (badface *)(* splittetlist)[i];
        if (!isdead(&remtet->tt) && org(remtet->tt) == remtet->forg &&
            dest(remtet->tt) == remtet->fdest && 
            apex(remtet->tt) == remtet->fapex &&
            oppo(remtet->tt) == remtet->foppo) {
          // Calculate the six dihedral angles of this tet.
          adjustedgering(remtet->tt, CCW);
          remtet->forg = org(remtet->tt);
          remtet->fdest = dest(remtet->tt);
          remtet->fapex = apex(remtet->tt);
          remtet->foppo = oppo(remtet->tt);
          tetalldihedral(remtet->forg, remtet->fdest, remtet->fapex,
            remtet->foppo, cosdd, NULL, NULL);
          // Is it a large angle?
          if (cosdd[0] < cosobjdihed) {
            slivercount++;
            remtet->key = cosdd[0];
            if (b->verbose > 1) {
              //printf("    Split tet (%d, %d, %d, %d) %g (degree).\n",
                   //pointmark(remtet->forg), pointmark(remtet->fdest),
                  // pointmark(remtet->fapex), pointmark(remtet->foppo),
                  // acos(remtet->key) / PI * 180.0);
            }
            /*if (b->verbose && ((acos(cosdd[0]) / PI * 180) > 179)) {
              // For DEBUG only.
              //printf("  p:draw_tet(%d, %d, %d, %d) -- %d (",
                     pointmark(remtet->forg), pointmark(remtet->fdest),
                     pointmark(remtet->fapex), pointmark(remtet->foppo),
                     slivercount);
              // Print the 6 dihedral angles.
              for (j = 0; j < 5; j++) {
                //printf("%4.1f, ", acos(cosdd[j]) / PI * 180.0);
              }
              //printf("%4.1f)\n", acos(cosdd[5]) / PI * 180.0);
            }*/
            // Queue this tet.
            newbadtet = (badface *) badtetrahedrons->alloc();
            *newbadtet = *remtet;
            // Try to remove this tet.
            if (!smoothsliver(remtet, tetlist)) {
              splitsliver(remtet, tetlist, ceillist);
            }
          }
        }
      } // i

      delete splittetlist;
      delete tetlist;
      delete ceillist;
    }

    optpasscount++;
  } while ((badtetrahedrons->items > 0) && (optpasscount < b->optpasses));

  if (b->verbose) {
    if (opt_tet_peels > 0l) {
      //printf("  %ld tet removals.\n", opt_tet_peels);
    }
    if (opt_face_flips > 0l) {
      //printf("  %ld face flips.\n", opt_face_flips);
    }
    if (opt_edge_flips > 0l) {
      //printf("  %ld edge flips.\n", opt_edge_flips);
    }
    if ((points->items - oldpointcount) > 0l) {
      //printf("  %ld point insertions", points->items - oldpointcount);
      if (smoothsegverts > 0) {
        //printf(" (%d on segment)", smoothsegverts);
      }
      //printf("\n");
    }
  }

  delete badtetrahedrons;
  badtetrahedrons = (memorypool *) NULL;
}

////                                                                       ////
////                                                                       ////
//// optimize_cxx /////////////////////////////////////////////////////////////

//// output_cxx ///////////////////////////////////////////////////////////////
////                                                                       ////
////                                                                       ////

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// jettisonnodes()    Jettison unused or duplicated vertices.                //
//                                                                           //
// Unused points are those input points which are outside the mesh domain or //
// have no connection (isolated) to the mesh.  Duplicated points exist for   //
// example if the input PLC is read from a .stl mesh file (marked during the //
// Delaunay tetrahedralization step. This routine remove these points from   //
// points list. All existing points are reindexed.                           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::jettisonnodes()
{
  point pointloop;
  bool jetflag;
  int oldidx, newidx;
  int remcount;

  if (!b->quiet) {
    //printf("Jettisoning redundants points.\n");
  }

  points->traversalinit();
  pointloop = pointtraverse();
  oldidx = newidx = 0; // in->firstnumber;
  remcount = 0;
  while (pointloop != (point) NULL) {
    jetflag = (pointtype(pointloop) == DUPLICATEDVERTEX) || 
      (pointtype(pointloop) == UNUSEDVERTEX);
    if (jetflag) {
      // It is a duplicated point, delete it.
      pointdealloc(pointloop);
      remcount++;
    } else {
      // Re-index it.
      setpointmark(pointloop, newidx + in->firstnumber);
      if (in->pointmarkerlist != (int *) NULL) {
        if (oldidx < in->numberofpoints) {
          // Re-index the point marker as well.
          in->pointmarkerlist[newidx] = in->pointmarkerlist[oldidx];
        }
      }
      newidx++;
    }
    oldidx++;
    if (oldidx == in->numberofpoints) {
      // Update the numbe of input points (Because some were removed).
      in->numberofpoints -= remcount;
      // Remember this number for output original input nodes.
      jettisoninverts = remcount;
    }
    pointloop = pointtraverse();
  }
  if (b->verbose) {
    //printf("  %d duplicated vertices have been removed.\n", dupverts);
    //printf("  %d unused vertices have been removed.\n", unuverts);
  }
  dupverts = 0;
  unuverts = 0;

  // The following line ensures that dead items in the pool of nodes cannot
  //   be allocated for the new created nodes. This ensures that the input
  //   nodes will occur earlier in the output files, and have lower indices.
  points->deaditemstack = (void *) NULL;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// highorder()   Create extra nodes for quadratic subparametric elements.    //
//                                                                           //
// 'highordertable' is an array (size = numberoftetrahedra * 6) for storing  //
// high-order nodes of each tetrahedron.  This routine is used only when -o2 //
// switch is used.                                                           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::highorder()
{
  triface tetloop, worktet;
  triface spintet, adjtet;
  point torg, tdest, tapex;
  point *extralist, *adjextralist;
  point newpoint;
  int hitbdry, ptmark;
  int i, j;

  if (!b->quiet) {
    //printf("Adding vertices for second-order tetrahedra.\n");
  }

  // Initialize the 'highordertable'.
  highordertable = new point[tetrahedrons->items * 6];
  if (highordertable == (point *) NULL) {
    terminatetetgen(1);
  }

  // The following line ensures that dead items in the pool of nodes cannot
  //   be allocated for the extra nodes associated with high order elements.
  //   This ensures that the primary nodes (at the corners of elements) will
  //   occur earlier in the output files, and have lower indices, than the
  //   extra nodes.
  points->deaditemstack = (void *) NULL;

  // Assign an entry for each tetrahedron to find its extra nodes. At the
  //   mean while, initialize all extra nodes be NULL.
  i = 0;
  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  while (tetloop.tet != (tetrahedron *) NULL) {
    tetloop.tet[highorderindex] = (tetrahedron) &highordertable[i];
    for (j = 0; j < 6; j++) {
      highordertable[i + j] = (point) NULL;
    }
    i += 6;
    tetloop.tet = tetrahedrontraverse();
  }

  // To create a unique node on each edge. Loop over all tetrahedra, and
  //   look at the six edges of each tetrahedron.  If the extra node in
  //   the tetrahedron corresponding to this edge is NULL, create a node
  //   for this edge, at the same time, set the new node into the extra
  //   node lists of all other tetrahedra sharing this edge.  
  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  while (tetloop.tet != (tetrahedron *) NULL) {
    // Get the list of extra nodes.
    extralist = (point *) tetloop.tet[highorderindex];
    worktet.tet = tetloop.tet;
    for (i = 0; i < 6; i++) {
      if (extralist[i] == (point) NULL) {
        // Operate on this edge.
        worktet.loc = edge2locver[i][0];
        worktet.ver = edge2locver[i][1];
        // Create a new node on this edge.
        torg = org(worktet);
        tdest = dest(worktet);
        // Create a new node in the middle of the edge.
        newpoint = (point) points->alloc();
        // Interpolate its attributes.
        for (j = 0; j < 3 + in->numberofpointattributes; j++) {
          newpoint[j] = 0.5 * (torg[j] + tdest[j]);
        }
        ptmark = (int) points->items - (in->firstnumber == 1 ? 0 : 1);
        setpointmark(newpoint, ptmark);
        // Add this node to its extra node list.
        extralist[i] = newpoint;
        // Set 'newpoint' into extra node lists of other tetrahedra
        //   sharing this edge.
        tapex = apex(worktet);
        spintet = worktet;
        hitbdry = 0;
        while (hitbdry < 2) {
          if (fnextself(spintet)) {
            // Get the extra node list of 'spintet'.
            adjextralist = (point *) spintet.tet[highorderindex];
            // Find the index of its extra node list.
            j = locver2edge[spintet.loc][spintet.ver];
            // Only set 'newpoint' into 'adjextralist' if it is a NULL.
            //   Because two faces can belong to the same tetrahedron.
            if (adjextralist[j] == (point) NULL) {
              adjextralist[j] = newpoint;
            }
            if (apex(spintet) == tapex) {
              break;
            }
          } else {
            hitbdry++;
            if (hitbdry < 2) {
              esym(worktet, spintet);
	    }
          }
        }
      }
    }
    tetloop.tet = tetrahedrontraverse();
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// numberedges()    Count the number of edges, save in "meshedges".          //
//                                                                           //
// This routine is called when '-p' or '-r', and '-E' options are used.  The //
// total number of edges depends on the genus of the input surface mesh.     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::numberedges()
{
  triface tetloop, worktet, spintet;
  int hitbdry, i;

  if (!b->plc && !b->refine) {
    // Using the Euler formula (V-E+F-T=1) to get the total number of edges.
    long faces = (4l * tetrahedrons->items + hullsize) / 2l;
    meshedges = points->items + faces - tetrahedrons->items - 1l;
    return;
  }

  meshedges = 0l;
  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  while (tetloop.tet != (tetrahedron *) NULL) {
    // Count the number of Voronoi faces. Look at the six edges of each
    //   tetrahedron. Count the edge only if the tetrahedron's pointer is
    //   smaller than those of all other tetrahedra that share the edge.
    worktet.tet = tetloop.tet;
    for (i = 0; i < 6; i++) {
      worktet.loc = edge2locver[i][0];
      worktet.ver = edge2locver[i][1];
      adjustedgering(worktet, CW);
      spintet = worktet;
      hitbdry = 0;
      while (hitbdry < 2) {
        if (fnextself(spintet)) {
          if (apex(spintet) == apex(worktet)) break;
          if (spintet.tet < worktet.tet) break;
        } else {
          hitbdry++;
          if (hitbdry < 2) {
            esym(worktet, spintet);
            fnextself(spintet); // In the same tet.
	  }
        }
      }
      // Count this edge if no adjacent tets are smaller than this tet.
      if (spintet.tet >= worktet.tet) {
        meshedges++;
      }
    }
    tetloop.tet = tetrahedrontraverse();
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// outnodes()    Output the points to a .node file or a tetgenio structure.  //
//                                                                           //
// Note: each point has already been numbered on input (the first index is   //
// 'in->firstnumber').                                                       //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

__device__ void tetgenmesh::outnodes(tetgenio* out)
{
  FILE *outfile;
  char outnodefilename[FILENAMESIZE];
  shellface subptr;
  triface adjtet;
  face subloop;
  point pointloop;
  point *extralist, ep[3];
  int nextras, bmark, shmark, marker;
  int coordindex, attribindex;
  int pointnumber, firstindex;
  int index, i;

  if (out == (tetgenio *) NULL) {
    /*strcpy(outnodefilename, b->outfilename);
    strcat(outnodefilename, ".node");*/
  } 
  
  /*if (!b->quiet) {
    if (out == (tetgenio *) NULL) {
      //printf("Writing %s.\n", outnodefilename);
    } else {
      //printf("Writing nodes.\n");
    }
  }*/

  nextras = in->numberofpointattributes;
  bmark = !b->nobound && in->pointmarkerlist;

  // Avoid compile warnings.
  outfile = (FILE *) NULL;
  marker = coordindex = 0;

  //if (out == (tetgenio *) NULL) {
  //  outfile = fopen(outnodefilename, "w");
  //  if (outfile == (FILE *) NULL) {
  //    //printf("File I/O Error:  Cannot create file %s.\n", outnodefilename);
  //    terminatetetgen(3);
  //  }
  //  // Number of points, number of dimensions, number of point attributes,
  //  //   and number of boundary markers (zero or one).
  //  fprintf(outfile, "%ld  %d  %d  %d\n", points->items, 3, nextras, bmark);
  //} else {
    // Allocate space for 'pointlist';
  //marktag
    //out->pointlist = new REAL[points->items * 3];
    if (out->pointlist == (REAL *) NULL) {
      return;
		//terminatetetgen(1);
    }
    // Allocate space for 'pointattributelist' if necessary;
	//marktag
    /*if (nextras > 0) {
      out->pointattributelist = new REAL[points->items * nextras];
      if (out->pointattributelist == (REAL *) NULL) {
        terminatetetgen(1);
      }
    }*/
    // Allocate space for 'pointmarkerlist' if necessary;
	//marktag
   /* if (bmark) {
      out->pointmarkerlist = new int[points->items];
      if (out->pointmarkerlist == (int *) NULL) {
        terminatetetgen(1);
      }
    }*/
    out->numberofpoints = points->items;
    out->numberofpointattributes = nextras;
    coordindex = 0;
    attribindex = 0;
 // }

  if (bmark && (b->plc || b->refine)) {
    // Initialize the point2tet field of each point.
    points->traversalinit();
    pointloop = pointtraverse();
    while (pointloop != (point) NULL) {
      setpoint2tet(pointloop, (tetrahedron) NULL);
      pointloop = pointtraverse();
    }
    // Make a map point-to-subface. Hence a boundary point will get the
    //   facet marker from that facet where it lies on.
    subfaces->traversalinit();
    subloop.sh = shellfacetraverse(subfaces);
    while (subloop.sh != (shellface *) NULL) {
      subloop.shver = 0;
      // Check all three points of the subface.
      for (i = 0; i < 3; i++) {
        pointloop = (point) subloop.sh[3 + i];
        setpoint2tet(pointloop, (tetrahedron) sencode(subloop));
      }
      if (b->order == 2) {
        // '-o2' switch. Set markers for quadratic nodes of this subface.
        stpivot(subloop, adjtet);
        if (adjtet.tet == dummytet) {
          sesymself(subloop);
          stpivot(subloop, adjtet);
        }
		//marktag
        ////assert(adjtet.tet != dummytet);
        extralist = (point *) adjtet.tet[highorderindex];
        switch (adjtet.loc) {
        case 0:
          ep[0] = extralist[0];
          ep[1] = extralist[1];
          ep[2] = extralist[2];
          break;
        case 1:
          ep[0] = extralist[0];
          ep[1] = extralist[4];
          ep[2] = extralist[3];
          break;
        case 2:
          ep[0] = extralist[1];
          ep[1] = extralist[5];
          ep[2] = extralist[4];
          break;
        case 3:
          ep[0] = extralist[2];
          ep[1] = extralist[3];
          ep[2] = extralist[5];
          break;
        default: break;
        }
        for (i = 0; i < 3; i++) {
          setpoint2tet(ep[i], (tetrahedron) sencode(subloop));
        }
      }
      subloop.sh = shellfacetraverse(subfaces);
    }
  }

  // Determine the first index (0 or 1).
  firstindex = b->zeroindex ? 0 : in->firstnumber; 

  points->traversalinit();
  pointloop = pointtraverse();
  pointnumber = firstindex; // in->firstnumber;
  index = 0;
  while (pointloop != (point) NULL) {
    if (bmark) {
      // Default the vertex has a zero marker.
      marker = 0;
      // Is it an input vertex?
      if (index < in->numberofpoints) {
        // Input point's marker is directly copied to output.
        marker = in->pointmarkerlist[index];
      }
      // Is it a boundary vertex has marker zero?
      if ((marker == 0) && (b->plc || b->refine)) {
        subptr = (shellface) point2tet(pointloop);
        if (subptr != (shellface) NULL) {
          // Default a boundary vertex has marker 1.
          marker = 1;
          if (in->facetmarkerlist != (int *) NULL) {
            // The vertex gets the marker from the facet it lies on.
            sdecode(subptr, subloop);
            shmark = shellmark(subloop);
            marker = in->facetmarkerlist[shmark - 1];
          }
        }
      }
    }
    //if (out == (tetgenio *) NULL) {
    //  // Point number, x, y and z coordinates.
    //  fprintf(outfile, "%4d    %.17g  %.17g  %.17g", pointnumber,
    //          pointloop[0], pointloop[1], pointloop[2]);
    //  for (i = 0; i < nextras; i++) {
    //    // Write an attribute.
    //    fprintf(outfile, "  %.17g", pointloop[3 + i]);
    //  }
    //  if (bmark) {
    //    // Write the boundary marker.
    //    fprintf(outfile, "    %d", marker);
    //  }
    //  fprintf(outfile, "\n");
    //} else {
      // X, y, and z coordinates.
      out->pointlist[coordindex++] = pointloop[0];
      out->pointlist[coordindex++] = pointloop[1];
      out->pointlist[coordindex++] = pointloop[2];
      // Point attributes.
      for (i = 0; i < nextras; i++) {
        // Output an attribute.
        out->pointattributelist[attribindex++] = pointloop[3 + i];
      }
      if (bmark) {
        // Output the boundary marker.  
        out->pointmarkerlist[index] = marker;
      }
    //}
    pointloop = pointtraverse();
    pointnumber++; 
    index++;
  }

  /*if (out == (tetgenio *) NULL) {
    fprintf(outfile, "# Generated by %s\n", b->commandline);
    fclose(outfile);
  }*/
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// outmetrics()    Output the metric to a file (*.mtr) or a tetgenio obj.    //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::outmetrics(tetgenio* out)
{
  FILE *outfile;
  char outmtrfilename[FILENAMESIZE];
  list *tetlist, *ptlist;
  triface tetloop;
  point ptloop, neipt;
  REAL lave, len; // lmin, lmax, 
  int mtrindex;
  int i;  

  if (out == (tetgenio *) NULL) {
    strcpy(outmtrfilename, b->outfilename);
    strcat(outmtrfilename, ".mtr");
  }

  if (!b->quiet) {
    if (out == (tetgenio *) NULL) {
      //printf("Writing %s.\n", outmtrfilename);
    } else {
      //printf("Writing metrics.\n");
    }
  }

  // Avoid compile warnings.
  outfile = (FILE *) NULL;
  mtrindex = 0;

  if (out == (tetgenio *) NULL) {
    outfile = fopen(outmtrfilename, "w");
    if (outfile == (FILE *) NULL) {
      //printf("File I/O Error:  Cannot create file %s.\n", outmtrfilename);
      terminatetetgen(3);
    }
    // Number of points, number of point metrices,
    // fprintf(outfile, "%ld  %d\n", points->items, sizeoftensor + 3);
    fprintf(outfile, "%ld  %d\n", points->items, 1);
  } else {
    // Allocate space for 'pointmtrlist' if necessary;
    // out->pointmtrlist = new REAL[points->items * (sizeoftensor + 3)];
    out->pointmtrlist = new REAL[points->items];
    if (out->pointmtrlist == (REAL *) NULL) {
      terminatetetgen(1);
    }
    out->numberofpointmtrs = 1; // (sizeoftensor + 3);
    mtrindex = 0;
  }
  
  // Initialize the point2tet field of each point.
  points->traversalinit();
  ptloop = pointtraverse();
  while (ptloop != (point) NULL) {
    setpoint2tet(ptloop, (tetrahedron) NULL);
    ptloop = pointtraverse();
  }
  // Create the point-to-tet map.
  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  while (tetloop.tet != (tetrahedron *) NULL) {
    for (i = 0; i < 4; i++) {
      ptloop = (point) tetloop.tet[4 + i];
      setpoint2tet(ptloop, encode(tetloop));
    }
    tetloop.tet = tetrahedrontraverse();
  }

  tetlist = new list(sizeof(triface), NULL, 256);
  ptlist = new list(sizeof(point *), NULL, 256);

  points->traversalinit();
  ptloop = pointtraverse();
  while (ptloop != (point) NULL) {
    decode(point2tet(ptloop), tetloop);
    if (!isdead(&tetloop)) {
      // Form the star of p.
      tetlist->append(&tetloop);
      formstarpolyhedron(ptloop, tetlist, ptlist, true);
      // lmin = longest;
      // lmax = 0.0;
      lave = 0.0;
      for (i = 0; i < ptlist->len(); i++) {
        neipt = * (point *)(* ptlist)[i];
        len = distance(ptloop, neipt);
        // lmin = lmin < len ? lmin : len;
        // lmax = lmax > len ? lmax : len;
        lave += len;
      }
      lave /= ptlist->len();
    }
    if (out == (tetgenio *) NULL) {
      for (i = 0; i < sizeoftensor; i++) {
        fprintf(outfile, "%-16.8e ", ptloop[pointmtrindex + i]);
      }
      if (ptlist->len() > 0) {
        // fprintf(outfile, "%-16.8e %-16.8e %-16.8e", lmin, lmax, lave);
        fprintf(outfile, "%-16.8e ", lave);
      } else {
        fprintf(outfile, "0.0 "); // fprintf(outfile, "0.0  0.0  0.0");
      }
      fprintf(outfile, "\n");
    } else {
      // for (i = 0; i < sizeoftensor; i++) {
      //   out->pointmtrlist[mtrindex++] = ptloop[pointmtrindex + i];
      // }
      if (ptlist->len() > 0) {
        // out->pointmtrlist[mtrindex++] = lmin;
        // out->pointmtrlist[mtrindex++] = lmax;
        out->pointmtrlist[mtrindex++] = lave;
      } else {
        // out->pointmtrlist[mtrindex++] = 0.0;
        // out->pointmtrlist[mtrindex++] = 0.0;
        out->pointmtrlist[mtrindex++] = 0.0;
      }
    }
    tetlist->clear();
    ptlist->clear();
    ptloop = pointtraverse();
  }

  delete tetlist;
  delete ptlist;

  if (out == (tetgenio *) NULL) {
    fprintf(outfile, "# Generated by %s\n", b->commandline);
    fclose(outfile);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// outelements()    Output the tetrahedra to an .ele file or a tetgenio      //
//                  structure.                                               //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

__device__ void tetgenmesh::outelements(tetgenio* out)
{
  FILE *outfile;
  char outelefilename[FILENAMESIZE];
  tetrahedron* tptr;
  triface worktet, spintet;
  int *tlist;
  REAL *talist;
  int firstindex, shift;
  int pointindex;
  int attribindex;
  point p1, p2, p3, p4;
  point *extralist;
  int elementnumber;
  int eextras;
  int hitbdry, i;

  if (out == (tetgenio *) NULL) {
    /*strcpy(outelefilename, b->outfilename);
    strcat(outelefilename, ".ele");*/
  }

  /*if (!b->quiet) {
    if (out == (tetgenio *) NULL) {
      //printf("Writing %s.\n", outelefilename);
    } else {
      //printf("Writing elements.\n");
    }
  }*/

  // Avoid compile warnings.
  outfile = (FILE *) NULL;
  tlist = (int *) NULL;
  talist = (double *) NULL;
  pointindex = attribindex = 0;

  eextras = in->numberoftetrahedronattributes;
  //if (out == (tetgenio *) NULL) {
  //  outfile = fopen(outelefilename, "w");
  //  if (outfile == (FILE *) NULL) {
  //    //printf("File I/O Error:  Cannot create file %s.\n", outelefilename);
  //    terminatetetgen(3);
  //  }
  //  // Number of tetras, points per tetra, attributes per tetra.
  //  fprintf(outfile, "%ld  %d  %d\n", tetrahedrons->items,
  //          b->order == 1 ? 4 : 10, eextras);
  //} else {
    // Allocate memory for output tetrahedra.
  //marktag
    //out->tetrahedronlist = new int[tetrahedrons->items * 
      //                             (b->order == 1 ? 4 : 10)];
    if (out->tetrahedronlist == (int *) NULL) {
      return;
		//terminatetetgen(1);
    }
    // Allocate memory for output tetrahedron attributes if necessary.
	//marktag
    /*if (eextras > 0) {
      out->tetrahedronattributelist = new REAL[tetrahedrons->items * eextras];
      if (out->tetrahedronattributelist == (REAL *) NULL) {
        terminatetetgen(1);
      }
    }*/
    out->numberoftetrahedra = tetrahedrons->items;
    out->numberofcorners = b->order == 1 ? 4 : 10;
    out->numberoftetrahedronattributes = eextras;
    tlist = out->tetrahedronlist;
    talist = out->tetrahedronattributelist;
    pointindex = 0;
    attribindex = 0;
  //}

  // Determine the first index (0 or 1).
  firstindex = b->zeroindex ? 0 : in->firstnumber;
  shift = 0; // Default no shiftment.
  if ((in->firstnumber == 1) && (firstindex == 0)) {
    shift = 1; // Shift the output indices by 1.
  }

  // Count the total edge numbers.
  meshedges = 0l;

  tetrahedrons->traversalinit();
  tptr = tetrahedrontraverse();
  elementnumber = firstindex; // in->firstnumber;
  while (tptr != (tetrahedron *) NULL) {
    if (b->noelewritten == 2) {
      // Reverse the orientation, such that Orient3D() > 0.
      p1 = (point) tptr[5];
      p2 = (point) tptr[4];
    } else {
      p1 = (point) tptr[4];
      p2 = (point) tptr[5];
    }
    p3 = (point) tptr[6];
    p4 = (point) tptr[7];
    //if (out == (tetgenio *) NULL) {
    //  // Tetrahedron number, indices for four points.
    //  fprintf(outfile, "%5d   %5d %5d %5d %5d", elementnumber,
    //          pointmark(p1) - shift, pointmark(p2) - shift,
    //          pointmark(p3) - shift, pointmark(p4) - shift);
    //  if (b->order == 2) {
    //    extralist = (point *) tptr[highorderindex];
    //    // Tetrahedron number, indices for four points plus six extra points.
    //    fprintf(outfile, "  %5d %5d %5d %5d %5d %5d",
    //      pointmark(extralist[0]) - shift, pointmark(extralist[1]) - shift,
    //      pointmark(extralist[2]) - shift, pointmark(extralist[3]) - shift,
    //      pointmark(extralist[4]) - shift, pointmark(extralist[5]) - shift);
    //  }
    //  for (i = 0; i < eextras; i++) {
    //    fprintf(outfile, "    %.17g", elemattribute(tptr, i));
    //  }
    //  fprintf(outfile, "\n");
    //} else {
      tlist[pointindex++] = pointmark(p1) - shift;
      tlist[pointindex++] = pointmark(p2) - shift;
      tlist[pointindex++] = pointmark(p3) - shift;
      tlist[pointindex++] = pointmark(p4) - shift;
      if (b->order == 2) {
        extralist = (point *) tptr[highorderindex];
        tlist[pointindex++] = pointmark(extralist[0]) - shift;
        tlist[pointindex++] = pointmark(extralist[1]) - shift;
        tlist[pointindex++] = pointmark(extralist[2]) - shift;
        tlist[pointindex++] = pointmark(extralist[3]) - shift;
        tlist[pointindex++] = pointmark(extralist[4]) - shift;
        tlist[pointindex++] = pointmark(extralist[5]) - shift;
      }
      for (i = 0; i < eextras; i++) {
        talist[attribindex++] = elemattribute(tptr, i);
      }
    //}
    if (b->neighout) {
      // Remember the index of this element.
      //* (int *) (tptr + elemmarkerindex) = elementnumber;
      setelemmarker(tptr, elementnumber);
    }
    // Count the number of Voronoi faces. Look at the six edges of each
    //   tetrahedron. Count the edge only if the tetrahedron's pointer is
    //   smaller than those of all other tetrahedra that share the edge.
    worktet.tet = tptr;
    for (i = 0; i < 6; i++) {
      worktet.loc = edge2locver[i][0];
      worktet.ver = edge2locver[i][1];
      adjustedgering(worktet, CW);
      spintet = worktet;
      hitbdry = 0;
      while (hitbdry < 2) {
        if (fnextself(spintet)) {
          if (apex(spintet) == apex(worktet)) break;
          if (spintet.tet < worktet.tet) break;
        } else {
          hitbdry++;
          if (hitbdry < 2) {
            esym(worktet, spintet);
            fnextself(spintet); // In the same tet.
	  }
        }
      }
      // Count this edge if no adjacent tets are smaller than this tet.
      if (spintet.tet >= worktet.tet) {
        meshedges++;
      }
    }
    tptr = tetrahedrontraverse();
    elementnumber++;
  }
  if (b->neighout) {
    // Set the outside element marker.
    //* (int *) (dummytet + elemmarkerindex) = -1;
    setelemmarker(dummytet, -1);
  }

  /*if (out == (tetgenio *) NULL) {
    fprintf(outfile, "# Generated by %s\n", b->commandline);
    fclose(outfile);
  }*/
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// outfaces()    Output all faces to a .face file or a tetgenio structure.   //
//                                                                           //
// This routines outputs all triangular faces (including outer boundary      //
// faces and inner faces) of this mesh.                                      //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::outfaces(tetgenio* out)
{
  FILE *outfile;
  char facefilename[FILENAMESIZE];
  int *elist;
  int *emlist;
  int neigh1, neigh2;
  int index;
  triface tface, tsymface;
  face checkmark;
  point torg, tdest, tapex;
  long faces;
  int bmark, faceid, marker;
  int firstindex, shift;
  int facenumber;

  if (out == (tetgenio *) NULL) {
    strcpy(facefilename, b->outfilename);
    strcat(facefilename, ".face");
  }

  if (!b->quiet) {
    if (out == (tetgenio *) NULL) {
      //printf("Writing %s.\n", facefilename);
    } else {
      //printf("Writing faces.\n");
    }
  }

  // Avoid compile warnings.
  outfile = (FILE *) NULL;
  elist = (int *) NULL;
  emlist = (int *) NULL;
  index =  marker = 0;

  faces = (4l * tetrahedrons->items + hullsize) / 2l;
  bmark = !b->nobound && in->facetmarkerlist;

  if (out == (tetgenio *) NULL) {
    outfile = fopen(facefilename, "w");
    if (outfile == (FILE *) NULL) {
      //printf("File I/O Error:  Cannot create file %s.\n", facefilename);
      terminatetetgen(3);
    }
    fprintf(outfile, "%ld  %d\n", faces, bmark);
  } else {
    // Allocate memory for 'trifacelist'.
    out->trifacelist = new int[faces * 3];
    if (out->trifacelist == (int *) NULL) {
      terminatetetgen(1);
    }
    // Allocate memory for 'trifacemarkerlist' if necessary.
    if (bmark) {
      out->trifacemarkerlist = new int[faces];
      if (out->trifacemarkerlist == (int *) NULL) {
        terminatetetgen(1);
      }
    }
    if (b->neighout > 1) {
      // '-nn' switch.
      out->adjtetlist = new int[faces * 2];
      if (out->adjtetlist == (int *) NULL) {
        terminatetetgen(1);
      }
    }
    out->numberoftrifaces = faces;
    elist = out->trifacelist;
    emlist = out->trifacemarkerlist;
    index = 0;
  }

  // Determine the first index (0 or 1).
  firstindex = b->zeroindex ? 0 : in->firstnumber;
  shift = 0; // Default no shiftment.
  if ((in->firstnumber == 1) && (firstindex == 0)) {
    shift = 1; // Shift the output indices by 1.
  }

  tetrahedrons->traversalinit();
  tface.tet = tetrahedrontraverse();
  facenumber = firstindex; // in->firstnumber;
  // To loop over the set of faces, loop over all tetrahedra, and look at
  //   the four faces of each one. If there isn't another tetrahedron
  //   adjacent to this face, operate on the face.  If there is another
  //   adjacent tetrahedron, operate on the face only if the current
  //   tetrahedron has a smaller pointer than its neighbor.  This way, each
  //   face is considered only once.
  while (tface.tet != (tetrahedron *) NULL) {
    for (tface.loc = 0; tface.loc < 4; tface.loc ++) {
      sym(tface, tsymface);
      if ((tsymface.tet == dummytet) || (tface.tet < tsymface.tet)) {
        torg = org(tface);
        tdest = dest(tface);
        tapex = apex(tface);
        if (bmark) {
          // Get the boundary marker of this face. If it is an inner face,
          //   it has no boundary marker, set it be zero.
          if (b->useshelles) {
            // Shell face is used.
            tspivot(tface, checkmark);
            if (checkmark.sh == dummysh) {
              marker = 0;  // It is an inner face.
            } else {
              faceid = shellmark(checkmark) - 1;
              marker = in->facetmarkerlist[faceid];
            }
          } else {
            // Shell face is not used, only distinguish outer and inner face.
            marker = tsymface.tet != dummytet ? 1 : 0;
          }
        }
        if (b->neighout > 1) {
          // '-nn' switch. Output adjacent tets indices.
          //neigh1 = * (int *)(tface.tet + elemmarkerindex);
          neigh1 = getelemmarker(tface.tet);
          if (tsymface.tet != dummytet) {
            //neigh2 = * (int *)(tsymface.tet + elemmarkerindex);
            neigh2 = getelemmarker(tsymface.tet);
          } else {
            neigh2 = -1;  
          }
        }
        if (out == (tetgenio *) NULL) {
          // Face number, indices of three vertices.
          fprintf(outfile, "%5d   %4d  %4d  %4d", facenumber,
                  pointmark(torg) - shift, pointmark(tdest) - shift,
                  pointmark(tapex) - shift);
          if (bmark) {
            // Output a boundary marker.
            fprintf(outfile, "  %d", marker);
          }
          if (b->neighout > 1) {
            fprintf(outfile, "    %5d  %5d", neigh1, neigh2);
          }
          fprintf(outfile, "\n");
        } else {
          // Output indices of three vertices.
          elist[index++] = pointmark(torg) - shift;
          elist[index++] = pointmark(tdest) - shift;
          elist[index++] = pointmark(tapex) - shift;
          if (bmark) {
            emlist[facenumber - in->firstnumber] = marker;
          }
          if (b->neighout > 1) {
            out->adjtetlist[(facenumber - in->firstnumber) * 2]     = neigh1;
            out->adjtetlist[(facenumber - in->firstnumber) * 2 + 1] = neigh2;
          }
        }
        facenumber++;
      }
    }
    tface.tet = tetrahedrontraverse();
  }

  if (out == (tetgenio *) NULL) {
    fprintf(outfile, "# Generated by %s\n", b->commandline);
    fclose(outfile);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// outhullfaces()    Output outer boundary faces to a .face file or a        //
//                   tetgenio structure.                                     //
//                                                                           //
// The normal of each face is arranged to point inside of the domain (use    //
// right-hand rule).  This routines will outputs convex hull faces if the    //
// mesh is a Delaunay tetrahedralization.                                    //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::outhullfaces(tetgenio* out)
{
  FILE *outfile;
  char facefilename[FILENAMESIZE];
  int *elist;
  int index;
  triface tface, tsymface;
  face checkmark;
  point torg, tdest, tapex;
  int firstindex, shift;
  int facenumber;

  if (out == (tetgenio *) NULL) {
    /*strcpy(facefilename, b->outfilename);
    strcat(facefilename, ".face");*/
  }

  /*if (!b->quiet) {
    if (out == (tetgenio *) NULL) {
      //printf("Writing %s.\n", facefilename);
    } else {
      //printf("Writing faces.\n");
    }
  }*/

  // Avoid compile warnings.
  outfile = (FILE *) NULL;
  elist = (int *) NULL;
  index = 0;

  if (out == (tetgenio *) NULL) {
    /*outfile = fopen(facefilename, "w");
    if (outfile == (FILE *) NULL) {
      //printf("File I/O Error:  Cannot create file %s.\n", facefilename);
      terminatetetgen(3);
    }
    fprintf(outfile, "%ld  0\n", hullsize);*/
  } else {
    // Allocate memory for 'trifacelist'.
    //marktag
	//out->trifacelist = new int[hullsize * 3];
    if (out->trifacelist == (int *) NULL) {
      return;
		//terminatetetgen(1);
    }
    out->numberoftrifaces = hullsize;
    elist = out->trifacelist;
    index = 0;
  }

  // Determine the first index (0 or 1).
  firstindex = b->zeroindex ? 0 : in->firstnumber;
  shift = 0; // Default no shiftment.
  if ((in->firstnumber == 1) && (firstindex == 0)) {
    shift = 1; // Shift the output indices by 1.
  }

  tetrahedrons->traversalinit();
  tface.tet = tetrahedrontraverse();
  facenumber = firstindex; // in->firstnumber;
  // To loop over the set of hull faces, loop over all tetrahedra, and look
  //   at the four faces of each one. If there isn't another tetrahedron
  //   adjacent to this face, operate on the face.
  while (tface.tet != (tetrahedron *) NULL) {
    for (tface.loc = 0; tface.loc < 4; tface.loc ++) {
      sym(tface, tsymface);
      if (tsymface.tet == dummytet) {
        torg = org(tface);
        tdest = dest(tface);
        tapex = apex(tface);
        if (out == (tetgenio *) NULL) {
          // Face number, indices of three vertices.
          /*fprintf(outfile, "%5d   %4d  %4d  %4d", facenumber,
                  pointmark(torg) - shift, pointmark(tdest) - shift,
                  pointmark(tapex) - shift);
          fprintf(outfile, "\n");*/
        } else {
          // Output indices of three vertices.
          elist[index++] = pointmark(torg) - shift;
          elist[index++] = pointmark(tdest) - shift;
          elist[index++] = pointmark(tapex) - shift;
        }
        facenumber++;
      }
    }
    tface.tet = tetrahedrontraverse();
  }

  if (out == (tetgenio *) NULL) {
    /*fprintf(outfile, "# Generated by %s\n", b->commandline);
    fclose(outfile);*/
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// outsubfaces()    Output subfaces (i.e. boundary faces) to a .face file or //
//                  a tetgenio structure.                                    //
//                                                                           //
// The boundary faces are exist in 'subfaces'. For listing triangle vertices //
// in the same sense for all triangles in the mesh, the direction determined //
// by right-hand rule is pointer to the inside of the volume.                //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::outsubfaces(tetgenio* out)
{
  FILE *outfile;
  char facefilename[FILENAMESIZE];
  int *elist;
  int *emlist;
  int index, index1, index2;
  triface abuttingtet;
  face faceloop;
  point torg, tdest, tapex;
  int bmark, faceid, marker;
  int firstindex, shift;
  int neigh1, neigh2;
  int facenumber;

  if (out == (tetgenio *) NULL) {
    strcpy(facefilename, b->outfilename);
    strcat(facefilename, ".face");
  }

  if (!b->quiet) {
    if (out == (tetgenio *) NULL) {
      //printf("Writing %s.\n", facefilename);
    } else {
      //printf("Writing faces.\n");
    }
  }

  // Avoid compile warnings.
  outfile = (FILE *) NULL;
  elist = (int *) NULL;
  emlist = (int *) NULL;
  index = index1 = index2 = 0;
  faceid = marker = 0;
  neigh1 = neigh2 = 0;

  bmark = !b->nobound && in->facetmarkerlist;

  if (out == (tetgenio *) NULL) {
    outfile = fopen(facefilename, "w");
    if (outfile == (FILE *) NULL) {
      //printf("File I/O Error:  Cannot create file %s.\n", facefilename);
      terminatetetgen(3);
    }
    // Number of subfaces.
    fprintf(outfile, "%ld  %d\n", subfaces->items, bmark);
  } else {
    // Allocate memory for 'trifacelist'.
    out->trifacelist = new int[subfaces->items * 3];
    if (out->trifacelist == (int *) NULL) {
      terminatetetgen(1);
    }
    if (bmark) {
      // Allocate memory for 'trifacemarkerlist'.
      out->trifacemarkerlist = new int[subfaces->items];
      if (out->trifacemarkerlist == (int *) NULL) {
        terminatetetgen(1);
      }
    }
    if (b->neighout > 1) {
      // '-nn' switch.
      out->adjtetlist = new int[subfaces->items * 2];
      if (out->adjtetlist == (int *) NULL) {
        terminatetetgen(1);
      }
    }
    out->numberoftrifaces = subfaces->items;
    elist = out->trifacelist;
    emlist = out->trifacemarkerlist;
  }

  // Determine the first index (0 or 1).
  firstindex = b->zeroindex ? 0 : in->firstnumber;
  shift = 0; // Default no shiftment.
  if ((in->firstnumber == 1) && (firstindex == 0)) {
    shift = 1; // Shift the output indices by 1.
  }

  subfaces->traversalinit();
  faceloop.sh = shellfacetraverse(subfaces);
  facenumber = firstindex; // in->firstnumber;
  while (faceloop.sh != (shellface *) NULL) {
    stpivot(faceloop, abuttingtet);
    if (abuttingtet.tet == dummytet) {
      sesymself(faceloop);
      stpivot(faceloop, abuttingtet);
    }
    if (abuttingtet.tet != dummytet) {
      // If there is a tetrahedron containing this subface, orient it so
      //   that the normal of this face points to inside of the volume by
      //   right-hand rule.
      adjustedgering(abuttingtet, CCW);
      torg = org(abuttingtet);
      tdest = dest(abuttingtet);
      tapex = apex(abuttingtet);
    } else {
      // This may happen when only a surface mesh be generated.
      torg = sorg(faceloop);
      tdest = sdest(faceloop);
      tapex = sapex(faceloop);
    }
    if (bmark) {
      faceid = shellmark(faceloop) - 1;
      marker = in->facetmarkerlist[faceid];
    }
    if (b->neighout > 1) {
      // '-nn' switch. Output adjacent tets indices.
      neigh1 = -1;
      stpivot(faceloop, abuttingtet);
      if (abuttingtet.tet != dummytet) {
        //neigh1 = * (int *)(abuttingtet.tet + elemmarkerindex);
        neigh1 = getelemmarker(abuttingtet.tet);
      }
      neigh2 = -1;
      sesymself(faceloop);
      stpivot(faceloop, abuttingtet);
      if (abuttingtet.tet != dummytet) {
        //neigh2 = * (int *)(abuttingtet.tet + elemmarkerindex);
        neigh2 = getelemmarker(abuttingtet.tet);
      }
    }
    if (out == (tetgenio *) NULL) {
      fprintf(outfile, "%5d   %4d  %4d  %4d", facenumber,
              pointmark(torg) - shift, pointmark(tdest) - shift,
              pointmark(tapex) - shift);
      if (bmark) {
        fprintf(outfile, "    %d", marker);
      }
      if (b->neighout > 1) {
        fprintf(outfile, "    %5d  %5d", neigh1, neigh2);
      }
      fprintf(outfile, "\n");
    } else {
      // Output three vertices of this face;
      elist[index++] = pointmark(torg) - shift;
      elist[index++] = pointmark(tdest) - shift;
      elist[index++] = pointmark(tapex) - shift;
      if (bmark) {
        emlist[index1++] = marker;
      }
      if (b->neighout > 1) {
        out->adjtetlist[index2++] = neigh1;
        out->adjtetlist[index2++] = neigh2;
      }
    }
    facenumber++;
    faceloop.sh = shellfacetraverse(subfaces);
  }

  if (out == (tetgenio *) NULL) {
    fprintf(outfile, "# Generated by %s\n", b->commandline);
    fclose(outfile);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// outedges()    Output all edges to a .edge file or a structure.            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::outedges(tetgenio* out)
{
  FILE *outfile;
  char edgefilename[FILENAMESIZE];
  int *elist, *emlist;
  int index, index1;
  triface tetloop, worktet, spintet;
  face checkseg;
  point torg, tdest;
  int firstindex, shift;
  int edgenumber, faceid, marker;
  int hitbdry, i;

  if (out == (tetgenio *) NULL) {
    strcpy(edgefilename, b->outfilename);
    strcat(edgefilename, ".edge");
  }

  if (!b->quiet) {
    if (out == (tetgenio *) NULL) {
      //printf("Writing %s.\n", edgefilename);
    } else {
      //printf("Writing edges.\n");
    }
  }

  // Avoid compile warnings.
  outfile = (FILE *) NULL;
  elist = (int *) NULL;
  emlist = (int *) NULL;
  index = index1 = 0;
  faceid = marker = 0;

  if (out == (tetgenio *) NULL) {
    outfile = fopen(edgefilename, "w");
    if (outfile == (FILE *) NULL) {
      //printf("File I/O Error:  Cannot create file %s.\n", edgefilename);
      terminatetetgen(3);
    }
    // Write the number of edges, boundary markers (0 or 1).
    fprintf(outfile, "%ld  %d\n", meshedges, !b->nobound);
  } else {
    // Allocate memory for 'edgelist'.
    out->edgelist = new int[meshedges * 2];
    if (out->edgelist == (int *) NULL) {
      terminatetetgen(1);
    }
    if (!b->nobound) {
      out->edgemarkerlist = new int[meshedges];
    }
    out->numberofedges = meshedges;
    elist = out->edgelist;
    emlist = out->edgemarkerlist;
  }

  // Determine the first index (0 or 1).
  firstindex = b->zeroindex ? 0 : in->firstnumber;
  shift = 0; // Default no shiftment.
  if ((in->firstnumber == 1) && (firstindex == 0)) {
    shift = 1; // Shift (reduce) the output indices by 1.
  }

  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  edgenumber = firstindex; // in->firstnumber;
  while (tetloop.tet != (tetrahedron *) NULL) {
    // Count the number of Voronoi faces. Look at the six edges of each
    //   tetrahedron. Count the edge only if the tetrahedron's pointer is
    //   smaller than those of all other tetrahedra that share the edge.
    worktet.tet = tetloop.tet;
    for (i = 0; i < 6; i++) {
      worktet.loc = edge2locver[i][0];
      worktet.ver = edge2locver[i][1];
      adjustedgering(worktet, CW);
      spintet = worktet;
      hitbdry = 0;
      while (hitbdry < 2) {
        if (fnextself(spintet)) {
          if (apex(spintet) == apex(worktet)) break;
          if (spintet.tet < worktet.tet) break;
        } else {
          hitbdry++;
          if (hitbdry < 2) {
            esym(worktet, spintet);
            fnextself(spintet); // In the same tet.
	  }
        }
      }
      // Count this edge if no adjacent tets are smaller than this tet.
      if (spintet.tet >= worktet.tet) {
        torg = org(worktet);
        tdest = dest(worktet);
        if (out == (tetgenio *) NULL) {
          fprintf(outfile, "%5d   %4d  %4d", edgenumber,
                  pointmark(torg) - shift, pointmark(tdest) - shift);
        } else {
          // Output three vertices of this face;
          elist[index++] = pointmark(torg) - shift;
          elist[index++] = pointmark(tdest) - shift;
        }
        if (!b->nobound) {
          // Check if the edge is a segment.
          tsspivot(&worktet, &checkseg);
          if (checkseg.sh != dummysh) {
            marker = shellmark(checkseg);
            if (marker == 0) {  // Does it have no marker?
              marker = 1;  // Set the default marker for this segment.
            }
          } else {
            marker = 0;  // It's not a segment.
          }
          if (out == (tetgenio *) NULL) {
            fprintf(outfile, "  %d", marker);
          } else {
            emlist[index1++] = marker;
          }
        }
        if (out == (tetgenio *) NULL) {
          fprintf(outfile, "\n");
        }
        edgenumber++;
      }
    }
    tetloop.tet = tetrahedrontraverse();
  }

  if (out == (tetgenio *) NULL) {
    fprintf(outfile, "# Generated by %s\n", b->commandline);
    fclose(outfile);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// outsubsegments()    Output segments to a .edge file or a structure.       //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::outsubsegments(tetgenio* out)
{
  FILE *outfile;
  char edgefilename[FILENAMESIZE];
  int *elist;
  int index;
  face edgeloop;
  point torg, tdest;
  int firstindex, shift;
  int edgenumber;

  if (out == (tetgenio *) NULL) {
    strcpy(edgefilename, b->outfilename);
    strcat(edgefilename, ".edge");
  }

  if (!b->quiet) {
    if (out == (tetgenio *) NULL) {
      //printf("Writing %s.\n", edgefilename);
    } else {
      //printf("Writing edges.\n");
    }
  }

  // Avoid compile warnings.
  outfile = (FILE *) NULL;
  elist = (int *) NULL;
  index = 0;  

  if (out == (tetgenio *) NULL) {
    outfile = fopen(edgefilename, "w");
    if (outfile == (FILE *) NULL) {
      //printf("File I/O Error:  Cannot create file %s.\n", edgefilename);
      terminatetetgen(3);
    }
    // Number of subsegments.
    fprintf(outfile, "%ld\n", subsegs->items);
  } else {
    // Allocate memory for 'edgelist'.
    out->edgelist = new int[subsegs->items * 2];
    if (out->edgelist == (int *) NULL) {
      terminatetetgen(1);
    }
    out->numberofedges = subsegs->items;
    elist = out->edgelist;
  }

  // Determine the first index (0 or 1).
  firstindex = b->zeroindex ? 0 : in->firstnumber;
  shift = 0; // Default no shiftment.
  if ((in->firstnumber == 1) && (firstindex == 0)) {
    shift = 1; // Shift the output indices by 1.
  }

  subsegs->traversalinit();
  edgeloop.sh = shellfacetraverse(subsegs);
  edgenumber = firstindex; // in->firstnumber;
  while (edgeloop.sh != (shellface *) NULL) {
    torg = sorg(edgeloop);
    tdest = sdest(edgeloop);
    if (out == (tetgenio *) NULL) {
      fprintf(outfile, "%5d   %4d  %4d\n", edgenumber,
              pointmark(torg) - shift, pointmark(tdest) - shift);
    } else {
      // Output three vertices of this face;
      elist[index++] = pointmark(torg) - shift;
      elist[index++] = pointmark(tdest) - shift;
    }
    edgenumber++;
    edgeloop.sh = shellfacetraverse(subsegs);
  }

  if (out == (tetgenio *) NULL) {
    fprintf(outfile, "# Generated by %s\n", b->commandline);
    fclose(outfile);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// outneighbors()    Output tet neighbors to a .neigh file or a structure.   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::outneighbors(tetgenio* out)
{
  FILE *outfile;
  char neighborfilename[FILENAMESIZE];
  int *nlist;
  int index;
  triface tetloop, tetsym;
  int neighbor1, neighbor2, neighbor3, neighbor4;
  int firstindex;
  int elementnumber;

  if (out == (tetgenio *) NULL) {
    strcpy(neighborfilename, b->outfilename);
    strcat(neighborfilename, ".neigh");
  }

  if (!b->quiet) {
    if (out == (tetgenio *) NULL) {
      //printf("Writing %s.\n", neighborfilename);
    } else {
      //printf("Writing neighbors.\n");
    }
  }

  // Avoid compile warnings.
  outfile = (FILE *) NULL;
  nlist = (int *) NULL;
  index = 0;

  if (out == (tetgenio *) NULL) {
    outfile = fopen(neighborfilename, "w");
    if (outfile == (FILE *) NULL) {
      //printf("File I/O Error:  Cannot create file %s.\n", neighborfilename);
      terminatetetgen(3);
    }
    // Number of tetrahedra, four faces per tetrahedron.
    fprintf(outfile, "%ld  %d\n", tetrahedrons->items, 4);
  } else {
    // Allocate memory for 'neighborlist'.
    out->neighborlist = new int[tetrahedrons->items * 4];
    if (out->neighborlist == (int *) NULL) {
      terminatetetgen(1);
    }
    nlist = out->neighborlist;
  }

  // Determine the first index (0 or 1).
  firstindex = b->zeroindex ? 0 : in->firstnumber;

  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  elementnumber = firstindex; // in->firstnumber;
  while (tetloop.tet != (tetrahedron *) NULL) {
    tetloop.loc = 2;
    sym(tetloop, tetsym);
    //neighbor1 = * (int *) (tetsym.tet + elemmarkerindex);
    neighbor1 = getelemmarker(tetsym.tet);
    tetloop.loc = 3;
    sym(tetloop, tetsym);
    //neighbor2 = * (int *) (tetsym.tet + elemmarkerindex);
    neighbor2 = getelemmarker(tetsym.tet);
    tetloop.loc = 1;
    sym(tetloop, tetsym);
    //neighbor3 = * (int *) (tetsym.tet + elemmarkerindex);
    neighbor3 = getelemmarker(tetsym.tet);
    tetloop.loc = 0;
    sym(tetloop, tetsym);
    //neighbor4 = * (int *) (tetsym.tet + elemmarkerindex);
    neighbor4 = getelemmarker(tetsym.tet);
    if (out == (tetgenio *) NULL) {
      // Tetrahedra number, neighboring tetrahedron numbers.
      fprintf(outfile, "%4d    %4d  %4d  %4d  %4d\n", elementnumber,
              neighbor1, neighbor2, neighbor3, neighbor4);
    } else {
      nlist[index++] = neighbor1;
      nlist[index++] = neighbor2;
      nlist[index++] = neighbor3;
      nlist[index++] = neighbor4;
    }
    tetloop.tet = tetrahedrontraverse();
    elementnumber++;
  }

  if (out == (tetgenio *) NULL) {
    fprintf(outfile, "# Generated by %s\n", b->commandline);
    fclose(outfile);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// outvoronoi()    Output the Voronoi diagram to .v.node, .v.edge, v.face,   //
//                 and .v.cell.                                              //
//                                                                           //
// The Voronoi diagram is the geometric dual of the Delaunay triangulation.  //
// The Voronoi vertices are the circumcenters of Delaunay tetrahedra.  Each  //
// Voronoi edge connects two Voronoi vertices at two sides of a common Dela- //
// unay face. At a face of convex hull, it becomes a ray (goto the infinity).//
// A Voronoi face is the convex hull of all Voronoi vertices around a common //
// Delaunay edge. It is a closed polygon for any interal Delaunay edge. At a //
// ridge, it is unbounded.  Each Voronoi cell is the convex hull of all Vor- //
// onoi vertices around a common Delaunay vertex. It is a polytope for any   //
// internal Delaunay vertex. It is an unbounded polyhedron for a Delaunay    //
// vertex belonging to the convex hull.                                      //
//                                                                           //
// Comment: Special thanks to Victor Liu for finding and fixing few bugs.    //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::outvoronoi(tetgenio* out)
{
  FILE *outfile;
  char outfilename[FILENAMESIZE];
  tetgenio::voroedge *vedge;
  tetgenio::vorofacet *vfacet;
  list *tetlist, *ptlist;
  triface tetloop, worktet, spintet;
  point pt[4], ptloop, neipt;
  REAL ccent[3], infvec[3], vec1[3], vec2[3], L;
  long faces, edges;
  int *tetfaceindexarray, *tetedgeindexarray;
  int arraysize, *vertarray;
  int vpointcount, vedgecount, vfacecount, tcount;
  int index, shift;
  int end1, end2;
  int hitbdry, i, j, k;

  // Output Voronoi vertices to .v.node file.
  if (out == (tetgenio *) NULL) {
    strcpy(outfilename, b->outfilename);
    strcat(outfilename, ".v.node");
  }

  if (!b->quiet) {
    if (out == (tetgenio *) NULL) {
      //printf("Writing %s.\n", outfilename);
    } else {
      //printf("Writing Voronoi vertices.\n");
    }
  }

  // Determine the first index (0 or 1).
  shift = (b->zeroindex ? 0 : in->firstnumber);
  // The number of Delaunay faces (= the number of Voronoi edges).
  faces = (4l * tetrahedrons->items + hullsize) / 2l;
  // The number of Delaunay edges (= the number of Voronoi faces).
  // edges = points->items + faces - tetrahedrons->items - 1;
  edges = meshedges;
  outfile = (FILE *) NULL; // Avoid compile warnings.

  if (out == (tetgenio *) NULL) {
    outfile = fopen(outfilename, "w");
    if (outfile == (FILE *) NULL) {
      //printf("File I/O Error:  Cannot create file %s.\n", outfilename);
      terminatetetgen(3);
    }
    // Number of voronoi points, 3 dim, no attributes, no marker.
    fprintf(outfile, "%ld  3  0  0\n", tetrahedrons->items);
  } else {
    // Allocate space for 'vpointlist'.
    out->numberofvpoints = (int) tetrahedrons->items;
    out->vpointlist = new REAL[out->numberofvpoints * 3];
    if (out->vpointlist == (REAL *) NULL) {
      terminatetetgen(1);
    }
  }

  // Loop the tetrahedronlist once, do the following: 
  //   (1) Output Voronoi vertices (the circumcenter of the tetrahedron).
  //   (2) Make a map from points-to-tetrahedra (for Voronoi cells).
  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  vpointcount = 0;
  index = 0;
  while (tetloop.tet != (tetrahedron *) NULL) {
    // Calculate the circumcenter.
    for (i = 0; i < 4; i++) {
      pt[i] = (point) tetloop.tet[4 + i];
      setpoint2tet(pt[i], encode(tetloop));
    }
    circumsphere(pt[0], pt[1], pt[2], pt[3], ccent, NULL);
    if (out == (tetgenio *) NULL) {
      fprintf(outfile, "%4d  %16.8e %16.8e %16.8e\n", vpointcount + shift,
              ccent[0], ccent[1], ccent[2]);
    } else {
      out->vpointlist[index++] = ccent[0];
      out->vpointlist[index++] = ccent[1];
      out->vpointlist[index++] = ccent[2];
    }
    // Remember the index of this element.
    * (int *) (tetloop.tet + elemmarkerindex) = vpointcount;
    vpointcount++;
    tetloop.tet = tetrahedrontraverse();
  }
  // Set the outside element marker.
  * (int *) (dummytet + elemmarkerindex) = -1;

  if (out == (tetgenio *) NULL) {
    fprintf(outfile, "# Generated by %s\n", b->commandline);
    fclose(outfile);
  }

  // Output Voronoi edges to .v.edge file.
  if (out == (tetgenio *) NULL) {
    strcpy(outfilename, b->outfilename);
    strcat(outfilename, ".v.edge");
  }
  
  if (!b->quiet) {
    if (out == (tetgenio *) NULL) {
      //printf("Writing %s.\n", outfilename);
    } else {
      //printf("Writing Voronoi edges.\n");
    }
  }

  if (out == (tetgenio *) NULL) {
    outfile = fopen(outfilename, "w");
    if (outfile == (FILE *) NULL) {
      //printf("File I/O Error:  Cannot create file %s.\n", outfilename);
      terminatetetgen(3);
    }
    // Number of Voronoi edges, no marker.
    fprintf(outfile, "%ld  0\n", faces);
  } else {
    // Allocate space for 'vpointlist'.
    out->numberofvedges = (int) faces;
    out->vedgelist = new tetgenio::voroedge[out->numberofvedges];
  }

  // Loop the tetrahedronlist once, output the Voronoi edges. The index of
  //   each Voronoi edge corresponding to the index of the Delaunay face.
  //   The four faces' indices of each tetrahedron are saved in the list
  //   'tetfaceindexarray', in the entry of i,  where i (0-based) is the
  //   index of this tetrahedron (= vpointcount). 
  tetfaceindexarray = new int[tetrahedrons->items * 4];  
  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  vedgecount = 0;
  index = 0;
  while (tetloop.tet != (tetrahedron *) NULL) {
    // Count the number of Voronoi edges. Look at the four faces of each
    //   tetrahedron. Count the face if the tetrahedron's pointer is
    //   smaller than its neighbor's or the neighbor is outside.
    end1 = * (int *) (tetloop.tet + elemmarkerindex);
    for (i = 0; i < 4; i++) {
      decode(tetloop.tet[i], worktet);
      if ((worktet.tet == dummytet) || (tetloop.tet < worktet.tet)) {
        if (out == (tetgenio *) NULL) {
          fprintf(outfile, "%4d  %4d", vedgecount + shift, end1 + shift);
        } else {
          vedge = &(out->vedgelist[index++]);
          vedge->v1 = end1 + shift;
        }
        end2 = * (int *) (worktet.tet + elemmarkerindex);
        // Note that end2 may be -1 (worktet.tet is outside).
        if (end2 == -1) {
          // Calculate the out normal of this hull face.
          worktet.tet = tetloop.tet;
          worktet.loc = i;
          worktet.ver = 1; // The CW edge ring.
          pt[0] = org(worktet);
          pt[1] = dest(worktet);
          pt[2] = apex(worktet);
          for (j = 0; j < 3; j++) vec1[j] = pt[1][j] - pt[0][j];
          for (j = 0; j < 3; j++) vec2[j] = pt[2][j] - pt[0][j];
          cross(vec1, vec2, infvec);
          // Normalize it.
          L = sqrt(infvec[0] * infvec[0] + infvec[1] * infvec[1]
                   + infvec[2] * infvec[2]);
          if (L > 0) for (j = 0; j < 3; j++) infvec[j] /= L;
          if (out == (tetgenio *) NULL) {
            fprintf(outfile, " -1");
            fprintf(outfile, " %g %g %g\n", infvec[0], infvec[1], infvec[2]);
          } else {
            vedge->v2 = -1;
            vedge->vnormal[0] = infvec[0];
            vedge->vnormal[1] = infvec[1];
            vedge->vnormal[2] = infvec[2];
          }
        } else {
          if (out == (tetgenio *) NULL) {
            fprintf(outfile, " %4d\n", end2 + shift);
          } else {
            vedge->v2 = end2 + shift;
            vedge->vnormal[0] = 0.0;
            vedge->vnormal[1] = 0.0;
            vedge->vnormal[2] = 0.0;
          }
        }
        // Save the face index in this tet and its neighbor if exists.
        tetfaceindexarray[end1 * 4 + i] = vedgecount;
        if (end2 != -1) {
          tetfaceindexarray[end2 * 4 + worktet.loc] = vedgecount;
        }
        vedgecount++;
      }
    }
    tetloop.tet = tetrahedrontraverse();
  }

  if (out == (tetgenio *) NULL) {
    fprintf(outfile, "# Generated by %s\n", b->commandline);
    fclose(outfile);
  }

  // Output Voronoi faces to .v.face file.
  if (out == (tetgenio *) NULL) {
    strcpy(outfilename, b->outfilename);
    strcat(outfilename, ".v.face");
  }
  
  if (!b->quiet) {
    if (out == (tetgenio *) NULL) {
      //printf("Writing %s.\n", outfilename);
    } else {
      //printf("Writing Voronoi faces.\n");
    }
  }

  if (out == (tetgenio *) NULL) {
    outfile = fopen(outfilename, "w");
    if (outfile == (FILE *) NULL) {
      //printf("File I/O Error:  Cannot create file %s.\n", outfilename);
      terminatetetgen(3);
    }
    // Number of Voronoi faces.
    fprintf(outfile, "%ld  0\n", edges);
  } else {
    out->numberofvfacets = edges;
    out->vfacetlist = new tetgenio::vorofacet[out->numberofvfacets];
    if (out->vfacetlist == (tetgenio::vorofacet *) NULL) {
      terminatetetgen(1);
    }
  }

  // Loop the tetrahedronlist once, Output Voronoi facets. The index of each
  //   Voronoi facet corresponding to the index of the Delaunay edge.  The
  //   six edges' indices of each tetrahedron are saved in the list 'tetedge-
  //   indexarray', in the entry of i,  where i (0-based) is the index of
  //   this tetrahedron (= vpointcount). 
  tetedgeindexarray = new int[tetrahedrons->items * 6];
  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  vfacecount = 0;
  while (tetloop.tet != (tetrahedron *) NULL) {
    // Count the number of Voronoi faces. Look at the six edges of each
    //   tetrahedron. Count the edge only if the tetrahedron's pointer is
    //   smaller than those of all other tetrahedra that share the edge.
    worktet = tetloop;
    for (i = 0; i < 6; i++) {
      worktet.loc = edge2locver[i][0];
      worktet.ver = edge2locver[i][1];
      // Now count the number of tets surrounding this edge.
      tcount = 1;
      adjustedgering(worktet, CW);
      spintet = worktet;
      hitbdry = 0;
      while (hitbdry < 2) {
        if (fnextself(spintet)) {
          if (apex(spintet) == apex(worktet)) break;
          if (spintet.tet < worktet.tet) break;
          tcount++;
        } else {
          hitbdry++;
          if (hitbdry < 2) {
            esym(worktet, spintet);
            fnextself(spintet); // In the same tet.
	  }
        }
      }
      // Count this edge if no adjacent tets are smaller than this tet.
      if (spintet.tet >= worktet.tet) {
        // Get the two endpoints of this edge.
        pt[0] = org(worktet);
        pt[1] = dest(worktet);
        end1 = pointmark(pt[0]) - in->firstnumber;
        end2 = pointmark(pt[1]) - in->firstnumber;
        if (out == (tetgenio *) NULL) {
          fprintf(outfile, "%4d  %4d %4d  %-2d ", vfacecount + shift, 
                  end1 + shift, end2 + shift, tcount + (hitbdry > 0));
        } else {
          vfacet = &(out->vfacetlist[vfacecount]);
          vfacet->c1 = end1 + shift;
          vfacet->c2 = end2 + shift;
          vfacet->elist = new int[tcount + (hitbdry > 0) + 1];
          vfacet->elist[0] = tcount + (hitbdry > 0);
          index = 1;
        }
        // If hitbdry > 0, then spintet is a hull face.
        if (hitbdry > 0) {
          // The edge list starts with a ray.
          vpointcount = * (int *) (spintet.tet + elemmarkerindex);
          vedgecount = tetfaceindexarray[vpointcount * 4 + spintet.loc];
          if (out == (tetgenio *) NULL) {
            fprintf(outfile, " %d", vedgecount + shift);
          } else {
            vfacet->elist[index++] = vedgecount + shift;
          }
          // Save this facet number in tet.
          tetedgeindexarray[vpointcount * 6 + 
            locver2edge[spintet.loc][spintet.ver]] = vfacecount;
          esymself(spintet);
          fnextself(spintet); // In the same tet.
        }
        // Output internal Voronoi edges.
        for (j = 0; j < tcount; j++) {
          vpointcount = * (int *) (spintet.tet + elemmarkerindex);
          vedgecount = tetfaceindexarray[vpointcount * 4 + spintet.loc];
          if (out == (tetgenio *) NULL) {
            fprintf(outfile, " %d", vedgecount + shift);
          } else {
            vfacet->elist[index++] = vedgecount + shift;
          }
          // Save this facet number in tet.
          tetedgeindexarray[vpointcount * 6 + 
            locver2edge[spintet.loc][spintet.ver]] = vfacecount;
          fnextself(spintet);
        }
        if (out == (tetgenio *) NULL) {
          fprintf(outfile, "\n");
        }
        vfacecount++;
      }
    } // if (i = 0; i < 6; i++)
    tetloop.tet = tetrahedrontraverse();
  }

  if (out == (tetgenio *) NULL) {
    fprintf(outfile, "# Generated by %s\n", b->commandline);
    fclose(outfile);
  }

  // Output Voronoi cells to .v.cell file.
  if (out == (tetgenio *) NULL) {
    strcpy(outfilename, b->outfilename);
    strcat(outfilename, ".v.cell");
  }
  
  if (!b->quiet) {
    if (out == (tetgenio *) NULL) {
      //printf("Writing %s.\n", outfilename);
    } else {
      //printf("Writing Voronoi cells.\n");
    }
  }

  if (out == (tetgenio *) NULL) {
    outfile = fopen(outfilename, "w");
    if (outfile == (FILE *) NULL) {
      //printf("File I/O Error:  Cannot create file %s.\n", outfilename);
      terminatetetgen(3);
    }
    // Number of Voronoi cells.
    fprintf(outfile, "%ld\n", points->items);
  } else {
    out->numberofvcells = points->items;
    out->vcelllist = new int*[out->numberofvcells];
    if (out->vcelllist == (int **) NULL) {
      terminatetetgen(1);
    }
  }

  // Loop through point list, for each point, output a Voronoi cell.
  tetlist = new list(sizeof(triface), NULL, 256);
  ptlist = new list(sizeof(point *), NULL, 256);
  points->traversalinit();
  ptloop = pointtraverse();
  vpointcount = 0;
  while (ptloop != (point) NULL) {
    decode(point2tet(ptloop), tetloop);
    // //assert(!isdead(&tetloop));
    if (!isdead(&tetloop)) {
      // Form the star of p.
      tetlist->append(&tetloop);
      formstarpolyhedron(ptloop, tetlist, ptlist, true);
      tcount = ptlist->len();
      if (out == (tetgenio *) NULL) {
        fprintf(outfile, "%4d  %-2d ", vpointcount + shift, tcount);
      } else {
        arraysize = tcount;
        vertarray = new int[arraysize + 1];
        out->vcelllist[vpointcount] = vertarray;
        vertarray[0] = arraysize;
        index = 1;
      }
      // List Voronoi facets bounding this cell.
      for (i = 0; i < ptlist->len(); i++) {
        neipt =  * (point *)(* ptlist)[i];
        // Find a tet in tetlist having edge (ptloop, neipt) -- Very Slow.
        for (j = 0; j < tetlist->len(); j++) {
          tetloop = * (triface *)(* tetlist)[j];
          for (k = 0; k < 6; k++) {
            tetloop.loc = edge2locver[k][0];
            tetloop.ver = edge2locver[k][1];
            if (org(tetloop) == ptloop) {
              if (dest(tetloop) == neipt) break;
            } else if (org(tetloop) == neipt) {
              if (dest(tetloop) == ptloop) break;
            }
          }
          if (k < 6) break; // Found this edge.
        }
        //assert(j < tetlist->len());
        // k is the right edge number.        
        end1 = * (int *) (tetloop.tet + elemmarkerindex);
        vfacecount = tetedgeindexarray[end1 * 6 + k];
        if (out == (tetgenio *) NULL) {
          fprintf(outfile, " %d", vfacecount + shift);
        } else {
          vertarray[index++] = vfacecount + shift;
        }
      } // for (i = 0; i < ptlist->len(); i++) {
      if (out == (tetgenio *) NULL) {
        fprintf(outfile, "\n");
      }
      vpointcount++;
    }
    tetlist->clear();
    ptlist->clear();
    ptloop = pointtraverse();
  }
  delete tetlist;
  delete ptlist;
  delete [] tetfaceindexarray;
  delete [] tetedgeindexarray;

  if (out == (tetgenio *) NULL) {
    fprintf(outfile, "# Generated by %s\n", b->commandline);
    fclose(outfile);
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// outsmesh()    Write surface mesh to a .smesh file, which can be read and  //
//               tetrahedralized by TetGen.                                  //
//                                                                           //
// You can specify a filename (without suffix) in 'smfilename'. If you don't //
// supply a filename (let smfilename be NULL), the default name stored in    //
// 'tetgenbehavior' will be used.                                            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::outsmesh(char* smfilename)
{
  FILE *outfile;
  char nodfilename[FILENAMESIZE];
  char smefilename[FILENAMESIZE];
  face faceloop;
  point p1, p2, p3;
  int firstindex, shift;
  int bmark;
  int faceid, marker;
  int i;

  if (smfilename != (char *) NULL && smfilename[0] != '\0') {
    strcpy(smefilename, smfilename);
  } else if (b->outfilename[0] != '\0') {
    strcpy(smefilename, b->outfilename);
  } else {
    strcpy(smefilename, "unnamed");
  }
  strcpy(nodfilename, smefilename);
  strcat(smefilename, ".smesh");
  strcat(nodfilename, ".node");

  if (!b->quiet) {
    //printf("Writing %s.\n", smefilename);
  }
  outfile = fopen(smefilename, "w");
  if (outfile == (FILE *) NULL) {
    //printf("File I/O Error:  Cannot create file %s.\n", smefilename);
    return;
  }

  // Determine the first index (0 or 1).
  firstindex = b->zeroindex ? 0 : in->firstnumber;
  shift = 0; // Default no shiftment.
  if ((in->firstnumber == 1) && (firstindex == 0)) {
    shift = 1; // Shift the output indices by 1.
  }

  fprintf(outfile, "# %s.  TetGen's input file.\n", smefilename);
  fprintf(outfile, "\n# part 1: node list.\n");
  fprintf(outfile, "0  3  0  0  # nodes are found in %s.\n", nodfilename);

  marker = 0; // avoid compile warning.
  bmark = !b->nobound && in->facetmarkerlist;  

  fprintf(outfile, "\n# part 2: facet list.\n");
  // Number of facets, boundary marker.
  fprintf(outfile, "%ld  %d\n", subfaces->items, bmark);
  
  subfaces->traversalinit();
  faceloop.sh = shellfacetraverse(subfaces);
  while (faceloop.sh != (shellface *) NULL) {
    p1 = sorg(faceloop);
    p2 = sdest(faceloop);
    p3 = sapex(faceloop);
    if (bmark) {
      faceid = shellmark(faceloop) - 1;
      if (faceid >= 0) { 
        marker = in->facetmarkerlist[faceid];
      } else {
        marker = 0; // This subface must be added manually later.
      }
    }
    fprintf(outfile, "3    %4d  %4d  %4d", pointmark(p1) - shift,
            pointmark(p2) - shift, pointmark(p3) - shift);
    if (bmark) {
      fprintf(outfile, "    %d", marker);
    }
    fprintf(outfile, "\n");
    faceloop.sh = shellfacetraverse(subfaces);
  }

  // Copy input holelist.
  fprintf(outfile, "\n# part 3: hole list.\n");
  fprintf(outfile, "%d\n", in->numberofholes);
  for (i = 0; i < in->numberofholes; i++) {
    fprintf(outfile, "%d  %g  %g  %g\n", i + in->firstnumber,
            in->holelist[i * 3], in->holelist[i * 3 + 1],
            in->holelist[i * 3 + 2]);
  }

  // Copy input regionlist.
  fprintf(outfile, "\n# part 4: region list.\n");
  fprintf(outfile, "%d\n", in->numberofregions);
  for (i = 0; i < in->numberofregions; i++) {
    fprintf(outfile, "%d  %g  %g  %g  %d  %g\n", i + in->firstnumber,
            in->regionlist[i * 5], in->regionlist[i * 5 + 1],
            in->regionlist[i * 5 + 2], (int) in->regionlist[i * 5 + 3],
            in->regionlist[i * 5 + 4]);
  }

  fprintf(outfile, "# Generated by %s\n", b->commandline);
  fclose(outfile);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// outmesh2medit()    Write mesh to a .mesh file, which can be read and      //
//                    rendered by Medit (a free mesh viewer from INRIA).     //
//                                                                           //
// You can specify a filename (without suffix) in 'mfilename'.  If you don't //
// supply a filename (let mfilename be NULL), the default name stored in     //
// 'tetgenbehavior' will be used. The output file will have the suffix .mesh.//
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::outmesh2medit(char* mfilename)
{
  FILE *outfile;
  char mefilename[FILENAMESIZE];
  tetrahedron* tetptr;
  triface tface, tsymface;
  face segloop, checkmark;
  point ptloop, p1, p2, p3, p4;
  long faces;
  int pointnumber;
  int i;

  if (mfilename != (char *) NULL && mfilename[0] != '\0') {
    strcpy(mefilename, mfilename);
  } else if (b->outfilename[0] != '\0') {
    strcpy(mefilename, b->outfilename);
  } else {
    strcpy(mefilename, "unnamed");
  }
  strcat(mefilename, ".mesh");

  if (!b->quiet) {
    //printf("Writing %s.\n", mefilename);
  }
  outfile = fopen(mefilename, "w");
  if (outfile == (FILE *) NULL) {
    //printf("File I/O Error:  Cannot create file %s.\n", mefilename);
    return;
  }

  fprintf(outfile, "MeshVersionFormatted 1\n");
  fprintf(outfile, "\n");
  fprintf(outfile, "Dimension\n");
  fprintf(outfile, "3\n");
  fprintf(outfile, "\n");

  fprintf(outfile, "\n# Set of mesh vertices\n");
  fprintf(outfile, "Vertices\n");
  fprintf(outfile, "%ld\n", points->items);

  points->traversalinit();
  ptloop = pointtraverse();
  pointnumber = 1;                        // Medit need start number form 1.
  while (ptloop != (point) NULL) {
    // Point coordinates.
    fprintf(outfile, "%.17g  %.17g  %.17g", ptloop[0], ptloop[1], ptloop[2]);
    if (in->numberofpointattributes > 0) {
      // Write an attribute, ignore others if more than one.
      fprintf(outfile, "  %.17g\n", ptloop[3]);
    } else {
      fprintf(outfile, "    0\n");
    }
    setpointmark(ptloop, pointnumber);
    ptloop = pointtraverse();
    pointnumber++;
  }

  // Compute the number of edges.
  faces = (4l * tetrahedrons->items + hullsize) / 2l;

  fprintf(outfile, "\n# Set of Triangles\n");
  fprintf(outfile, "Triangles\n");
  fprintf(outfile, "%ld\n", faces);

  tetrahedrons->traversalinit();
  tface.tet = tetrahedrontraverse();
  // To loop over the set of faces, loop over all tetrahedra, and look at
  //   the four faces of each tetrahedron. If there isn't another tetrahedron
  //   adjacent to the face, operate on the face.  If there is another adj-
  //   acent tetrahedron, operate on the face only if the current tetrahedron
  //   has a smaller pointer than its neighbor.  This way, each face is
  //   considered only once.
  while (tface.tet != (tetrahedron *) NULL) {
    for (tface.loc = 0; tface.loc < 4; tface.loc ++) {
      sym(tface, tsymface);
      if (tface.tet < tsymface.tet || tsymface.tet == dummytet) {
        p1 = org (tface);
        p2 = dest(tface);
        p3 = apex(tface);
        fprintf(outfile, "%5d  %5d  %5d",
                pointmark(p1), pointmark(p2), pointmark(p3));
        fprintf(outfile, "    0\n");
      }
    }
    tface.tet = tetrahedrontraverse();
  }

  fprintf(outfile, "\n# Set of Tetrahedra\n");
  fprintf(outfile, "Tetrahedra\n");
  fprintf(outfile, "%ld\n", tetrahedrons->items);

  tetrahedrons->traversalinit();
  tetptr = tetrahedrontraverse();
  while (tetptr != (tetrahedron *) NULL) {
    p1 = (point) tetptr[4];
    p2 = (point) tetptr[5];
    p3 = (point) tetptr[6];
    p4 = (point) tetptr[7];
    fprintf(outfile, "%5d  %5d  %5d  %5d",
            pointmark(p1), pointmark(p2), pointmark(p3), pointmark(p4));
    if (in->numberoftetrahedronattributes > 0) {
      fprintf(outfile, "  %.17g", elemattribute(tetptr, 0));
    } else {
      fprintf(outfile, "  0");
    }
    fprintf(outfile, "\n");
    tetptr = tetrahedrontraverse();
  }

  fprintf(outfile, "\nCorners\n");
  fprintf(outfile, "%d\n", in->numberofpoints);

  for (i = 0; i < in->numberofpoints; i++) {
    fprintf(outfile, "%4d\n", i + 1);
  }

  if (b->useshelles) {
    fprintf(outfile, "\nEdges\n");
    fprintf(outfile, "%ld\n", subsegs->items);

    subsegs->traversalinit();
    segloop.sh = shellfacetraverse(subsegs);
    while (segloop.sh != (shellface *) NULL) {
      p1 = sorg(segloop);
      p2 = sdest(segloop);
      fprintf(outfile, "%5d  %5d", pointmark(p1), pointmark(p2));
      fprintf(outfile, "    0\n");
      segloop.sh = shellfacetraverse(subsegs);
    }
  }

  fprintf(outfile, "\nEnd\n");
  fclose(outfile);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// outmesh2gid()    Write mesh to a .ele.msh file and a .face.msh file,      //
//                  which can be imported and rendered by Gid.               //
//                                                                           //
// You can specify a filename (without suffix) in 'gfilename'.  If you don't //
// supply a filename (let gfilename be NULL), the default name stored in     //
// 'tetgenbehavior' will be used. The suffixes (.ele.msh and .face.msh) will //
// be automatically added.                                                   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::outmesh2gid(char* gfilename)
{
  FILE *outfile;
  char gidfilename[FILENAMESIZE];
  tetrahedron* tetptr;
  triface tface, tsymface;
  face sface;
  point ptloop, p1, p2, p3, p4;
  int pointnumber;
  int elementnumber;

  if (gfilename != (char *) NULL && gfilename[0] != '\0') {
    strcpy(gidfilename, gfilename);
  } else if (b->outfilename[0] != '\0') {
    strcpy(gidfilename, b->outfilename);
  } else {
    strcpy(gidfilename, "unnamed");
  }
  strcat(gidfilename, ".ele.msh");

  if (!b->quiet) {
    //printf("Writing %s.\n", gidfilename);
  }
  outfile = fopen(gidfilename, "w");
  if (outfile == (FILE *) NULL) {
    //printf("File I/O Error:  Cannot create file %s.\n", gidfilename);
    return;
  }

  fprintf(outfile, "mesh dimension = 3 elemtype tetrahedron nnode = 4\n");
  fprintf(outfile, "coordinates\n");

  points->traversalinit();
  ptloop = pointtraverse();
  pointnumber = 1;                        // Gid need start number form 1.
  while (ptloop != (point) NULL) {
    // Point coordinates.
    fprintf(outfile, "%4d  %.17g %.17g %.17g", pointnumber,
            ptloop[0], ptloop[1], ptloop[2]);
    if (in->numberofpointattributes > 0) {
      // Write an attribute, ignore others if more than one.
      fprintf(outfile, "  %.17g", ptloop[3]);
    }
    fprintf(outfile, "\n");
    setpointmark(ptloop, pointnumber);
    ptloop = pointtraverse();
    pointnumber++;
  }

  fprintf(outfile, "end coordinates\n");
  fprintf(outfile, "elements\n");

  tetrahedrons->traversalinit();
  tetptr = tetrahedrontraverse();
  elementnumber = 1;
  while (tetptr != (tetrahedron *) NULL) {
    p1 = (point) tetptr[4];
    p2 = (point) tetptr[5];
    p3 = (point) tetptr[6];
    p4 = (point) tetptr[7];
    fprintf(outfile, "%5d  %5d %5d %5d %5d", elementnumber,
            pointmark(p1), pointmark(p2), pointmark(p3), pointmark(p4));
    if (in->numberoftetrahedronattributes > 0) {
      fprintf(outfile, "  %.17g", elemattribute(tetptr, 0));
    } 
    fprintf(outfile, "\n");
    tetptr = tetrahedrontraverse();
    elementnumber++;
  }

  fprintf(outfile, "end elements\n");
  fclose(outfile);

  if (gfilename != (char *) NULL && gfilename[0] != '\0') {
    strcpy(gidfilename, gfilename);
  } else if (b->outfilename[0] != '\0') {
    strcpy(gidfilename, b->outfilename);
  } else {
    strcpy(gidfilename, "unnamed");
  }
  strcat(gidfilename, ".face.msh");

  if (!b->quiet) {
    //printf("Writing %s.\n", gidfilename);
  }
  outfile = fopen(gidfilename, "w");
  if (outfile == (FILE *) NULL) {
    //printf("File I/O Error:  Cannot create file %s.\n", gidfilename);
    return;
  }

  fprintf(outfile, "mesh dimension = 3 elemtype triangle nnode = 3\n");
  fprintf(outfile, "coordinates\n");

  points->traversalinit();
  ptloop = pointtraverse();
  pointnumber = 1;                        // Gid need start number form 1.
  while (ptloop != (point) NULL) {
    // Point coordinates.
    fprintf(outfile, "%4d  %.17g %.17g %.17g", pointnumber,
            ptloop[0], ptloop[1], ptloop[2]);
    if (in->numberofpointattributes > 0) {
      // Write an attribute, ignore others if more than one.
      fprintf(outfile, "  %.17g", ptloop[3]);
    }
    fprintf(outfile, "\n");
    setpointmark(ptloop, pointnumber);
    ptloop = pointtraverse();
    pointnumber++;
  }

  fprintf(outfile, "end coordinates\n");
  fprintf(outfile, "elements\n");

  tetrahedrons->traversalinit();
  tface.tet = tetrahedrontraverse();
  elementnumber = 1;
  while (tface.tet != (tetrahedron *) NULL) {
    for (tface.loc = 0; tface.loc < 4; tface.loc ++) {
      sym(tface, tsymface);
      if ((tface.tet < tsymface.tet) || (tsymface.tet == dummytet)) {
        p1 = org(tface);
        p2 = dest(tface);
        p3 = apex(tface);
        if (tsymface.tet == dummytet) {
          // It's a hull face, output it.
          fprintf(outfile, "%5d   %d  %d  %d\n", elementnumber,
                  pointmark(p1), pointmark(p2), pointmark(p3));
          elementnumber++;
        } else if (b->useshelles) {
          // Only output it if it's a subface.
          tspivot(tface, sface);
          if (sface.sh != dummysh) {
            fprintf(outfile, "%5d   %d  %d  %d\n", elementnumber,
                    pointmark(p1), pointmark(p2), pointmark(p3));
            elementnumber++;
          }
        }
      }
    }
    tface.tet = tetrahedrontraverse();
  }

  fprintf(outfile, "end elements\n");
  fclose(outfile);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// outmesh2off()    Write the mesh to an .off file.                          //
//                                                                           //
// .off, the Object File Format, is one of the popular file formats from the //
// Geometry Center's Geomview package (http://www.geomview.org).             //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::outmesh2off(char* ofilename)
{
  FILE *outfile;
  char offfilename[FILENAMESIZE];
  triface tface, tsymface;
  point ptloop, p1, p2, p3;
  long faces;
  int shift;

  if (ofilename != (char *) NULL && ofilename[0] != '\0') {
    strcpy(offfilename, ofilename);
  } else if (b->outfilename[0] != '\0') {
    strcpy(offfilename, b->outfilename);
  } else {
    strcpy(offfilename, "unnamed");
  }
  strcat(offfilename, ".off");

  if (!b->quiet) {
    //printf("Writing %s.\n", offfilename);
  }
  outfile = fopen(offfilename, "w");
  if (outfile == (FILE *) NULL) {
    //printf("File I/O Error:  Cannot create file %s.\n", offfilename);
    return;
  }

  // Calculate the number of triangular faces in the tetrahedral mesh.
  faces = (4l * tetrahedrons->items + hullsize) / 2l;

  // Number of points, faces, and edges(not used, here show hullsize).
  fprintf(outfile, "OFF\n%ld  %ld  %ld\n", points->items, faces, hullsize);

  // Write the points.
  points->traversalinit();
  ptloop = pointtraverse();
  while (ptloop != (point) NULL) {
    fprintf(outfile, " %.17g  %.17g  %.17g\n",ptloop[0], ptloop[1], ptloop[2]);
    ptloop = pointtraverse();
  }

  // OFF always use zero as the first index.
  shift = in->firstnumber == 1 ? 1 : 0;

  tetrahedrons->traversalinit();
  tface.tet = tetrahedrontraverse();
  // To loop over the set of faces, loop over all tetrahedra, and look at
  //   the four faces of each tetrahedron. If there isn't another tetrahedron
  //   adjacent to the face, operate on the face.  If there is another adj-
  //   acent tetrahedron, operate on the face only if the current tetrahedron
  //   has a smaller pointer than its neighbor.  This way, each face is
  //   considered only once.
  while (tface.tet != (tetrahedron *) NULL) {
    for (tface.loc = 0; tface.loc < 4; tface.loc ++) {
      sym(tface, tsymface);
      if ((tface.tet < tsymface.tet) || (tsymface.tet == dummytet)) {
        p1 = org(tface);
        p2 = dest(tface);
        p3 = apex(tface);
        // Face number, indices of three vertexs.
        fprintf(outfile, "3   %4d  %4d  %4d\n", pointmark(p1) - shift,
                pointmark(p2) - shift, pointmark(p3) - shift);
      }
    }
    tface.tet = tetrahedrontraverse();
  }

  fprintf(outfile, "# Generated by %s\n", b->commandline);
  fclose(outfile);
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// outmesh2vtk()    Save mesh to file in VTK Legacy format.                  //
//                                                                           //
// This function was contributed by Bryn Llyod from ETH, 2007.               //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::outmesh2vtk(char* ofilename)
{
  FILE *outfile;
  char vtkfilename[FILENAMESIZE];
  point pointloop;
  tetrahedron* tptr;
  double x, y, z;
  int n1, n2, n3, n4;
  int nnodes = 4;
  int celltype = 10;

  int NEL = tetrahedrons->items;
  int NN = points->items;

  if (ofilename != (char *) NULL && ofilename[0] != '\0') {
    strcpy(vtkfilename, ofilename);
  } else if (b->outfilename[0] != '\0') {
    strcpy(vtkfilename, b->outfilename);
  } else {
    strcpy(vtkfilename, "unnamed");
  }
  strcat(vtkfilename, ".vtk");

  if (!b->quiet) {
    //printf("Writing %s.\n", vtkfilename);
  }
  outfile = fopen(vtkfilename, "w");
  if (outfile == (FILE *) NULL) {
    //printf("File I/O Error:  Cannot create file %s.\n", vtkfilename);
    return;
  }

  //always write big endian
  //bool ImALittleEndian = !testIsBigEndian();

  fprintf(outfile, "# vtk DataFile Version 2.0\n");
  fprintf(outfile, "Unstructured Grid\n");
  fprintf(outfile, "ASCII\n"); // BINARY
  fprintf(outfile, "DATASET UNSTRUCTURED_GRID\n");
  fprintf(outfile, "POINTS %d double\n", NN);

  points->traversalinit();
  pointloop = pointtraverse();
  for(int id=0; id<NN && pointloop != (point) NULL; id++){
    x = pointloop[0];
    y = pointloop[1];
    z = pointloop[2];
    //if(ImALittleEndian){
    //  swapBytes((unsigned char *) &x, sizeof(x));
    //  swapBytes((unsigned char *) &y, sizeof(y));
    //  swapBytes((unsigned char *) &z, sizeof(z));
    //}
    //fwrite((char*)(&x),sizeof(double),1,outfile);
    //fwrite((char*)(&y),sizeof(double),1,outfile);
    //fwrite((char*)(&z),sizeof(double),1,outfile);
    fprintf(outfile, "%.17g %.17g %.17g\n", x, y, z);
    pointloop = pointtraverse();
  }
  fprintf(outfile, "\n");

  fprintf(outfile, "CELLS %d %d\n", NEL, NEL*(4+1));
  //NEL rows, each has 1 type id + 4 node id's
 
  tetrahedrons->traversalinit();
  tptr = tetrahedrontraverse();
  //elementnumber = firstindex; // in->firstnumber;
  if (b->order == 2) {
    //printf("  Write VTK not implemented for order 2 elements \n");
    return;
  }
  while (tptr != (tetrahedron *) NULL) {
    point p1 = (point) tptr[4];
    point p2 = (point) tptr[5];
    point p3 = (point) tptr[6];
    point p4 = (point) tptr[7];
    n1 = pointmark(p1) - in->firstnumber;
    n2 = pointmark(p2) - in->firstnumber;
    n3 = pointmark(p3) - in->firstnumber;
    n4 = pointmark(p4) - in->firstnumber;
    //if(ImALittleEndian){
    //  swapBytes((unsigned char *) &nnodes, sizeof(nnodes));
    //  swapBytes((unsigned char *) &n1, sizeof(n1));
    //  swapBytes((unsigned char *) &n2, sizeof(n2));
    //  swapBytes((unsigned char *) &n3, sizeof(n3));
    //  swapBytes((unsigned char *) &n4, sizeof(n4));
    //}
    //fwrite((char*)(&nnodes),sizeof(int), 1, outfile);
    //fwrite((char*)(&n1),sizeof(int), 1, outfile);
    //fwrite((char*)(&n2),sizeof(int), 1, outfile);
    //fwrite((char*)(&n3),sizeof(int), 1, outfile);
    //fwrite((char*)(&n4),sizeof(int), 1, outfile);
    fprintf(outfile, "%d  %4d %4d %4d %4d\n", nnodes, n1, n2, n3, n4);
    tptr = tetrahedrontraverse();
  }
  fprintf(outfile, "\n");

  fprintf(outfile, "CELL_TYPES %d\n", NEL);
  for(int tid=0; tid<NEL; tid++){
    // if(ImALittleEndian)
    //   swapBytes((unsigned char *) &celltype,sizeof(celltype));
    // fwrite((char*)(&celltype), sizeof(int), 1, outfile);
    fprintf(outfile, "%d\n", celltype);
  }
  fprintf(outfile, "\n");

  fclose(outfile);
}

////                                                                       ////
////                                                                       ////
//// output_cxx ///////////////////////////////////////////////////////////////

//// report_cxx ///////////////////////////////////////////////////////////////
////                                                                       ////
////                                                                       ////

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// checkmesh()    Test the mesh for topological consistency.                 //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::checkmesh()
{
  triface tetraloop;
  triface oppotet, oppooppotet;
  point tetorg, tetdest, tetapex, tetoppo; //, *pts;
  REAL oritest;
  int horrors;

  if (!b->quiet) {
    //printf("  Checking consistency of mesh...\n");
  }

  horrors = 0;
  // Run through the list of tetrahedra, checking each one.
  tetrahedrons->traversalinit();
  tetraloop.tet = tetrahedrontraverse();
  while (tetraloop.tet != (tetrahedron *) NULL) {
    // Check all four faces of the tetrahedron.
    for (tetraloop.loc = 0; tetraloop.loc < 4; tetraloop.loc++) {
      tetorg = org(tetraloop);
      tetdest = dest(tetraloop);
      tetapex = apex(tetraloop);
      tetoppo = oppo(tetraloop);
      if (tetraloop.loc == 0) {             // Only test for inversion once.
        oritest = orient3d(tetorg, tetdest, tetapex, tetoppo);
        if (oritest >= 0.0) {
          //printf("  !! !! %s ", oritest > 0.0 ? "Inverted" : "Degenerated");
          printtet(&tetraloop);
          //printf("  orient3d = %.17g.\n", oritest);
          horrors++;
        }
      }
      // Find the neighboring tetrahedron on this face.
      sym(tetraloop, oppotet);
      if (oppotet.tet != dummytet) {
        // Check if it is a dead tet.
        if (!isdead(&oppotet)) {
          // Check that the tetrahedron's neighbor knows it's a neighbor.
          sym(oppotet, oppooppotet);
          if ((tetraloop.tet != oppooppotet.tet)
              || (tetraloop.loc != oppooppotet.loc)) {
            //printf("  !! !! Asymmetric tetra-tetra bond:\n");
            if (tetraloop.tet == oppooppotet.tet) {
              //printf("   (Right tetrahedron, wrong orientation)\n");
            }
            //printf("    First ");
            printtet(&tetraloop);
            //printf("    Second (nonreciprocating) ");
            printtet(&oppotet);
            horrors++;
          }
        } else {
          //printf("  !! !! A dead neighbor:\n");
          printtet(&tetraloop);
          horrors++;
        }
      }
    }
    /*if (infected(tetraloop)) {
      pts = (point *) &(tetraloop.tet[4]);
      //printf("  !! tet (%d, %d, %d, %d) is infected.\n", pointmark(pts[0]),
        pointmark(pts[1]), pointmark(pts[2]), pointmark(pts[3]));
      horrors++;
    }
    if (marktested(tetraloop)) {
      pts = (point *) &(tetraloop.tet[4]);
      //printf("  !! tet (%d, %d, %d, %d) is marktested.\n", pointmark(pts[0]),
        pointmark(pts[1]), pointmark(pts[2]), pointmark(pts[3]));
      horrors++;
    }*/
    tetraloop.tet = tetrahedrontraverse();
  }
  if (horrors == 0) {
    if (!b->quiet) {
      //printf("  In my studied opinion, the mesh appears to be consistent.\n");
    }
  } else if (horrors == 1) {
    //printf("  !! !! !! !! Precisely one festering wound discovered.\n");
  } else {
    //printf("  !! !! !! !! %d abominations witnessed.\n", horrors);
  }

  return horrors;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// checkshells()       Test the boundary mesh for topological consistency.   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::checkshells()
{
  triface oppotet, oppooppotet, testtet; 
  face shloop, segloop, spin;
  face testsh, testseg, testshsh;
  point shorg, shdest, segorg, segdest;
  REAL checksign;
  bool same;
  int horrors;
  int i, j;

  if (!b->quiet) {
    //printf("  Checking consistency of the mesh boundary...\n");
  }
  horrors = 0;

  // Run through the list of subfaces, checking each one.
  subfaces->traversalinit();
  shloop.sh = shellfacetraverse(subfaces);
  while (shloop.sh != (shellface *) NULL) {
    // Check two connected tetrahedra if they exist.
    shloop.shver = 0;
    stpivot(shloop, oppotet);
    if (oppotet.tet != dummytet) {
      // Check if the tet and the face have the same vertices.
      for (i = 0; i < 3; i++) {
        pinfect((point) shloop.sh[3 + i]);
      }
      for (j = 0; j < 3; j++) {
        if (org(oppotet) == NULL) break;
        if (!pinfected(org(oppotet))) break;
        enextself(oppotet);
      }
      for (i = 0; i < 3; i++) {
        puninfect((point) shloop.sh[3 + i]);
      }
      if (j < 3) {
        //printf("  !! !! Wrong subface-tet connection.\n");
        //printf("  p:draw_subface(%d, %d, %d).\n", pointmark(sorg(shloop)), 
          //pointmark(sdest(shloop)), pointmark(sapex(shloop)));
        //printf("  p:draw_tet(%d, %d, %d, %d).\n",
         // pointmark(org(oppotet)), pointmark(dest(oppotet)), 
         // pointmark(apex(oppotet)), pointmark(oppo(oppotet)));
        horrors++;
      }
      tspivot(oppotet, testsh);
      if (testsh.sh != shloop.sh) {
        //printf("  !! !! Wrong tetra-subface connection.\n");
        //printf("    Tetra: ");
        printtet(&oppotet);
        //printf("    Subface: ");
        printsh(&shloop);
        horrors++;
      }
      if (oppo(oppotet) != (point) NULL) {
        adjustedgering(oppotet, CCW);
        checksign = orient3d(sorg(shloop), sdest(shloop), sapex(shloop),
                             oppo(oppotet));
        if (checksign >= 0.0) {
          //printf("  !! !! Wrong subface orientation.\n");
          //printf("    Subface: ");
          printsh(&shloop);
          horrors++;
        }
      }
    }
    sesymself(shloop);
    stpivot(shloop, oppooppotet);
    if (oppooppotet.tet != dummytet) {
      tspivot(oppooppotet, testsh);
      if (testsh.sh != shloop.sh) {
        //printf("  !! !! Wrong tetra-subface connection.\n");
        //printf("    Tetra: ");
        printtet(&oppooppotet);
        //printf("    Subface: ");
        printsh(&shloop);
        horrors++;
      }
      if (oppotet.tet != dummytet) {
        sym(oppotet, testtet);
        if (testtet.tet != oppooppotet.tet) {
          //printf("  !! !! Wrong tetra-subface-tetra connection.\n");
          //printf("    Tetra 1: ");
          printtet(&oppotet);
          //printf("    Subface: ");
          printsh(&shloop);
          //printf("    Tetra 2: ");
          printtet(&oppooppotet);
          horrors++;
        }
      }
      if (oppo(oppooppotet) != (point) NULL) {
        adjustedgering(oppooppotet, CCW);
        checksign = orient3d(sorg(shloop), sdest(shloop), sapex(shloop),
                             oppo(oppooppotet));
        if (checksign >= 0.0) {
          //printf("  !! !! Wrong subface orientation.\n");
          //printf("    Subface: ");
          printsh(&shloop);
          horrors++;
        }
      }
    }
    // Check connection between subfaces.
    shloop.shver = 0;
    for (i = 0; i < 3; i++) {
      shorg = sorg(shloop);
      shdest = sdest(shloop);
      sspivot(shloop, testseg);
      if (testseg.sh != dummysh) {
        segorg = sorg(testseg);
        segdest = sdest(testseg);
        same = ((shorg == segorg) && (shdest == segdest)) 
	    || ((shorg == segdest) && (shdest == segorg));
        if (!same) {
          //printf("  !! !! Wrong subface-subsegment connection.\n");
          //printf("    Subface: ");
          printsh(&shloop);
          //printf("    Subsegment: ");
          printsh(&testseg);
          horrors++;
        } 
      } 
      spivot(shloop, testsh);
      if (testsh.sh != dummysh) {
        // Check if the subface is self-bonded.
        if (testsh.sh == shloop.sh) {
          //printf("  !! !! Subface is self-bonded.\n");
          printsh(&shloop);
          horrors++;
        }
        segorg = sorg(testsh);
        segdest = sdest(testsh);
        same = ((shorg == segorg) && (shdest == segdest)) 
	    || ((shorg == segdest) && (shdest == segorg));
        if (!same) {
          //printf("  !! !! Wrong subface-subface connection.\n");
          //printf("    Subface 1: ");
          printsh(&shloop);
          //printf("    Subface 2: ");
          printsh(&testsh);
          horrors++;
        }
        spivot(testsh, testshsh);
        shorg = sorg(testshsh);
        shdest = sdest(testshsh);
        same = ((shorg == segorg) && (shdest == segdest)) 
	    || ((shorg == segdest) && (shdest == segorg));
        if (!same) {
          //printf("  !! !! Wrong subface-subface connection.\n");
          //printf("    Subface 1: ");
          printsh(&testsh);
          //printf("    Subface 2: ");
          printsh(&testshsh);
          horrors++;
        }
        if (testseg.sh == dummysh) {
          if (testshsh.sh != shloop.sh) {
            //printf("  !! !! Wrong subface-subface connection.\n");
            //printf("    Subface 1: ");
            printsh(&shloop);
            //printf("    Subface 2: ");
            printsh(&testsh);
            horrors++;
          }
        } 
      }
      senextself(shloop);
    }
    if (sinfected(shloop)) {
      //printf("  !! subface (%d, %d, %d) is infected.\n",
       // pointmark(sorg(shloop)), pointmark(sdest(shloop)),
        //pointmark(sapex(shloop)));
      horrors++;
    }
    if (!b->quality) {
      // During refinement, subfaces/subsegs rejected to be split were
      //   marktested. In other cases, they should be not.
      if (smarktested(shloop)) {
        //printf("  !! subface (%d, %d, %d) is marktested.\n",
         // pointmark(sorg(shloop)), pointmark(sdest(shloop)),
         // pointmark(sapex(shloop)));
        horrors++;
      }
    }
    shloop.sh = shellfacetraverse(subfaces);
  }

  if (horrors > 0) {
    return horrors;
  }

  // Run through the list of subsegs, checking each one.
  subsegs->traversalinit();
  segloop.sh = shellfacetraverse(subsegs);
  while (segloop.sh != (shellface *) NULL) {
    segorg = sorg(segloop);
    segdest = sdest(segloop);
    spivot(segloop, testsh);
    if (testsh.sh == dummysh) {
      //printf("  !! !! Wrong subsegment-subface connection.\n");
      //printf("    Subsegment: ");
      printsh(&segloop);
      horrors++;
      segloop.sh = shellfacetraverse(subsegs);
      continue;
    }
    shorg = sorg(testsh);
    shdest = sdest(testsh);
    same = ((shorg == segorg) && (shdest == segdest)) 
        || ((shorg == segdest) && (shdest == segorg));
    if (!same) {
      //printf("  !! !! Wrong subsegment-subface connection.\n");
      //printf("    Subsegment : ");
      printsh(&segloop);
      //printf("    Subface : ");
      printsh(&testsh);
      horrors++;
      segloop.sh = shellfacetraverse(subsegs);
      continue;
    }
    // Check the connection of face loop around this subsegment.
    spin = testsh;
    i = 0;
    do {
      spivotself(spin);
      if (spin.sh != dummysh) {
        shorg = sorg(spin);
        shdest = sdest(spin);
        same = ((shorg == segorg) && (shdest == segdest)) 
            || ((shorg == segdest) && (shdest == segorg));
        if (!same) {
          //printf("  !! !! Wrong subsegment-subface connection.\n");
          //printf("    Subsegment : ");
          printsh(&segloop);
          //printf("    Subface : ");
          printsh(&testsh);
          horrors++;
          break;
        }
        i++;
      } else {
        break;
      }
    } while (spin.sh != testsh.sh && i < 1000);
    if (i >= 1000) {
      //printf("  !! !! Wrong subsegment-subface connection.\n");
      //printf("    Subsegment : ");
      printsh(&segloop);
      horrors++;
    }
    segloop.sh = shellfacetraverse(subsegs);
  }
  if (horrors == 0) {
    if (!b->quiet) {
      //printf("  Mesh boundaries connected correctly.\n");
    }
  } else {
    //printf("  !! !! !! !! %d boundary connection viewed with horror.\n",
        //   horrors);
  }
  return horrors;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// checksegments()    Check the connections between tetrahedra and segments. //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::checksegments()
{
  triface tetloop, neightet;
  face sseg, checkseg;
  point pa, pb;
  int hitbdry;
  int horrors, i;

  if (!b->quiet) {
    //printf("  Checking tet-seg connections...\n");
  }

  horrors = 0;
  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  while (tetloop.tet != NULL) {
    // Loop the six edges of the tet.
    if (tetloop.tet[8] != NULL) {
      for (i = 0; i < 6; i++) {
        tetloop.loc = edge2locver[i][0];
        tetloop.ver = edge2locver[i][1];
        tsspivot1(tetloop, sseg);
        if (sseg.sh != dummysh) {
          // Check if they are the same edge.
          sseg.shver = 0;
          pa = (point) sorg(sseg);
          pb = (point) sdest(sseg);
          if (!(((org(tetloop) == pa) && (dest(tetloop) == pb)) ||
                ((org(tetloop) == pb) && (dest(tetloop) == pa)))) {
            //printf("  !! Wrong tet-seg connection.\n");
            //printf("    Tet: x%lx (%d, %d, %d, %d) - Seg: x%lx (%d, %d).\n", 
              //(unsigned long) tetloop.tet, pointmark(org(tetloop)),
             // pointmark(dest(tetloop)), pointmark(apex(tetloop)),
             // pointmark(oppo(tetloop)), (unsigned long) sseg.sh,
             // pointmark(pa), pointmark(pb));
            horrors++;
          } else {
            // Loop all tets sharing at this edge.
            neightet = tetloop;
            hitbdry = 0;
            do {
              tsspivot1(neightet, checkseg);
              if (checkseg.sh != sseg.sh) {
                //printf("  !! Wrong tet-seg connection.\n");
                //printf("    Tet: x%lx (%d, %d, %d, %d) - ", 
                  //(unsigned long) tetloop.tet, pointmark(org(tetloop)),
                 // pointmark(dest(tetloop)), pointmark(apex(tetloop)),
                 // pointmark(oppo(tetloop)));
                if (checkseg.sh != NULL) {
                  //printf("Seg x%lx (%d, %d).\n", (unsigned long) checkseg.sh,
                  //pointmark(sorg(checkseg)), pointmark(sdest(checkseg))); 
                } else {
                  //printf("Seg: NULL.\n");
                }
                horrors++;
              }
              tfnextself(neightet);
              if (neightet.tet == dummytet) {
                hitbdry++;
                if (hitbdry == 2) break;
                esym(tetloop, neightet);
                tfnextself(neightet);
                if (neightet.tet == dummytet) break;
              }
            } while (neightet.tet != tetloop.tet);
          }
        }
      }
    }
    tetloop.tet = tetrahedrontraverse();
  }

  if (horrors == 0) {
    //printf("  Segments are connected properly.\n");
  } else {
    //printf("  !! !! !! !! Found %d missing connections.\n", horrors);
  }

  return horrors;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// checkdelaunay()    Ensure that the mesh is constrained Delaunay.          //
//                                                                           //
// If 'flipqueue' is not NULL, non-locally Delaunay faces are saved in it.   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int tetgenmesh::checkdelaunay(REAL eps, queue* flipqueue)
{
  triface tetraloop;
  triface oppotet;
  face opposhelle;
  point tetorg, tetdest, tetapex, tetoppo;
  point oppooppo;
  REAL sign;
  int shouldbedelaunay;
  int horrors;

  if (!b->quiet) {
    //printf("  Checking Delaunay property of the mesh...\n");
  }
  horrors = 0;
  // Run through the list of triangles, checking each one.
  tetrahedrons->traversalinit();
  tetraloop.tet = tetrahedrontraverse();
  while (tetraloop.tet != (tetrahedron *) NULL) {
    // Check all four faces of the tetrahedron.
    for (tetraloop.loc = 0; tetraloop.loc < 4; tetraloop.loc++) {
      tetorg = org(tetraloop);
      tetdest = dest(tetraloop);
      tetapex = apex(tetraloop);
      tetoppo = oppo(tetraloop);
      sym(tetraloop, oppotet);
      oppooppo = oppo(oppotet);
      // Only do testif there is an adjoining tetrahedron whose pointer is
      //   larger (to ensure that each pair isn't tested twice).
      shouldbedelaunay = (oppotet.tet != dummytet)
                          && (tetoppo != (point) NULL)
                          && (oppooppo != (point) NULL)
                          && (tetraloop.tet < oppotet.tet);
      if (checksubfaces && shouldbedelaunay) {
        // If a shell face separates the tetrahedra, then the face is
        //   constrained, so no local Delaunay test should be done.
        tspivot(tetraloop, opposhelle);
        if (opposhelle.sh != dummysh){
          shouldbedelaunay = 0;
        }
      }
      if (shouldbedelaunay) {
        sign = insphere(tetdest, tetorg, tetapex, tetoppo, oppooppo);
        if ((sign > 0.0) && (eps > 0.0)) {
          if (iscospheric(tetdest, tetorg, tetapex, tetoppo, oppooppo, sign,
                          eps)) sign = 0.0;
        }
        if (sign > 0.0) {
          if (flipqueue) {
            enqueueflipface(tetraloop, flipqueue);
          }
          horrors++;
        }
      }
    }
    tetraloop.tet = tetrahedrontraverse();
  }

  if (flipqueue == (queue *) NULL) {
    if (horrors == 0) {
      if (!b->quiet) {
        //printf("  The mesh is %s.\n",
               //checksubfaces ? "constrained Delaunay" : "Delaunay");
      }
    } else {
      //printf("  !! !! !! !! %d obscenities viewed with horror.\n", horrors);
    }
  }

  return horrors;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// checkconforming()    Ensure that the mesh is conforming Delaunay.         //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::checkconforming()
{
  face segloop, shloop;
  int encsubsegs, encsubfaces;

  if (!b->quiet) {
    //printf("  Checking conforming Delaunay property of mesh...\n");
  }
  encsubsegs = encsubfaces = 0;
  // Run through the list of subsegments, check each one.
  subsegs->traversalinit();
  segloop.sh = shellfacetraverse(subsegs);
  while (segloop.sh != (shellface *) NULL) {
    if (checkseg4encroach(&segloop, NULL, NULL, false)) {
      //printf("  !! !! Non-conforming subsegment: (%d, %d)\n",
             //pointmark(sorg(segloop)), pointmark(sdest(segloop)));
      encsubsegs++;
    }
    segloop.sh = shellfacetraverse(subsegs);
  }
  // Run through the list of subfaces, check each one.
  subfaces->traversalinit();
  shloop.sh = shellfacetraverse(subfaces);
  while (shloop.sh != (shellface *) NULL) {
    if (checksub4encroach(&shloop, NULL, false)) {
      //printf("  !! !! Non-conforming subface: (%d, %d, %d)\n",
            // pointmark(sorg(shloop)), pointmark(sdest(shloop)),
           //  pointmark(sapex(shloop)));
      encsubfaces++;
    }
    shloop.sh = shellfacetraverse(subfaces);
  }
  if (encsubsegs == 0 && encsubfaces == 0) {
    if (!b->quiet) {
      //printf("  The mesh is conforming Delaunay.\n");
    }
  } else {
    if (encsubsegs > 0) {
      //printf("  !! !! %d subsegments are non-conforming.\n", encsubsegs);
    }
    if (encsubfaces > 0) {
      //printf("  !! !! %d subfaces are non-conforming.\n", encsubfaces);
    }
  }
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// algorithmicstatistics()    Print statistics about the mesh algorithms.    //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::algorithmicstatistics()
{
  //printf("Algorithmic statistics:\n\n");

  //printf("  Number of orient3d tests: %ld\n", orient3dcount);
  //printf("  Number of insphere tests: %ld\n", inspherecount);
  //printf("  Number of symbolic insphere tests: %ld\n", insphere_sos_count);
  //printf("  Number of visited tets in point location: %ld\n", ptloc_count);
  //printf("  Maximal number of tets per point location: %ld\n",ptloc_max_count);
  //printf("  Number of hull sites: %ld\n", inserthullcount);
  //printf("  Number of 1-to-4 flips: %ld\n", flip14count);
  //printf("  Number of 2-to-6 flips: %ld\n", flip26count);
  //printf("  Number of n-t-2n flips: %ld\n", flipn2ncount);

  if (!b->plc) {
    if (1) {
      //printf("  Number of deleted tets: %ld\n", totaldeadtets);
      //printf("  Number of created tets: %ld\n", totalbowatcavsize);
      //printf("  Maximum number of tets per new point: %ld\n", maxbowatcavsize);
      // //printf("  Number of 3-to-2 flips: %ld\n", flip32count);
    } else {
      // //printf("  Number of 3-to-2 flips: %ld\n", flip32count);
      // //printf("  Number of 2-to-3 flips: %ld\n", flip23count);
      // //printf("  Number of n-to-m flips: %ld\n", flipnmcount);
      // //printf("  Total number of primitive flips: %ld\n",
      //   flip23count + flip32count);
    }
  }

  if (b->plc) {
    //printf("  Number of 2-to-2 flips: %ld\n", flip22count);
    // //printf("  Number of tri-edge inter (coplanar) tests: %ld (%ld)\n",
    //   triedgcount, triedgcopcount);
    //printf("  Number of crossed faces (edges) in scout segs: %ld (%ld)\n",
      //across_face_count, across_edge_count);
    //printf("  Maximal number of crossed faces per segment: %ld\n",
      //across_max_count);
    //printf("  Number of rule-1 points: %ld\n", r1count);
    //printf("  Number of rule-2 points: %ld\n", r2count);
    //printf("  Number of rule-3 points: %ld\n", r3count);
    //printf("  Maximal size of a missing region: %ld\n", maxregionsize);
    //printf("  Maximal size of a recovered cavity: %ld\n", maxcavsize);
    //printf("  Number of non-Delaunay edges: %ld\n", ndelaunayedgecount);
    //printf("  Number of cavity expansions: %ld\n", cavityexpcount);
  }
  
  // //printf("  Total point location time (millisec):  %g\n", tloctime * 1e+3);
  // //printf("  Total point insertion time (millisec):  %g\n",tinserttime*1e+3);
  // if (b->bowyerwatson == 0) {
  //   //printf("  Total flip time (millisec):  %g\n", tfliptime * 1e+3);
  // }

  //printf("\n");
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// qualitystatistics()    Print statistics about the quality of the mesh.    //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::qualitystatistics()
{
  triface tetloop, neightet;
  point p[4];
  char sbuf[128];
  REAL radiusratiotable[12];
  REAL aspectratiotable[12];
  REAL A[4][4], rhs[4], D;
  REAL V[6][3], N[4][3], H[4]; // edge-vectors, face-normals, face-heights.
  REAL edgelength[6], alldihed[6], faceangle[3];
  REAL shortest, longest;
  REAL smallestvolume, biggestvolume;
  REAL smallestratio, biggestratio;
  REAL smallestdiangle, biggestdiangle;
  REAL smallestfaangle, biggestfaangle;
  REAL tetvol, minaltitude;
  REAL cirradius, minheightinv; // insradius;
  REAL shortlen, longlen;
  REAL tetaspect, tetradius;
  REAL smalldiangle, bigdiangle;
  REAL smallfaangle, bigfaangle;
  int radiustable[12];
  int aspecttable[16];
  int dihedangletable[18];
  int faceangletable[18];
  int indx[4];
  int radiusindex;
  int aspectindex;
  int tendegree;
  int i, j;

  //printf("Mesh quality statistics:\n\n");

  // Avoid compile warnings.
  shortlen = longlen = 0.0;
  smalldiangle = bigdiangle = 0.0;

  radiusratiotable[0]  =    0.707;    radiusratiotable[1]  =     1.0;
  radiusratiotable[2]  =      1.1;    radiusratiotable[3]  =     1.2;
  radiusratiotable[4]  =      1.4;    radiusratiotable[5]  =     1.6;
  radiusratiotable[6]  =      1.8;    radiusratiotable[7]  =     2.0;
  radiusratiotable[8]  =      2.5;    radiusratiotable[9]  =     3.0;
  radiusratiotable[10] =     10.0;    radiusratiotable[11] =     0.0;

  aspectratiotable[0]  =      1.5;    aspectratiotable[1]  =     2.0;
  aspectratiotable[2]  =      2.5;    aspectratiotable[3]  =     3.0;
  aspectratiotable[4]  =      4.0;    aspectratiotable[5]  =     6.0;
  aspectratiotable[6]  =     10.0;    aspectratiotable[7]  =    15.0;
  aspectratiotable[8]  =     25.0;    aspectratiotable[9]  =    50.0;
  aspectratiotable[10] =    100.0;    aspectratiotable[11] =     0.0;
  
  for (i = 0; i < 12; i++) radiustable[i] = 0;
  for (i = 0; i < 12; i++) aspecttable[i] = 0;
  for (i = 0; i < 18; i++) dihedangletable[i] = 0;
  for (i = 0; i < 18; i++) faceangletable[i] = 0;

  minaltitude = xmax - xmin + ymax - ymin + zmax - zmin;
  minaltitude = minaltitude * minaltitude;
  shortest = minaltitude;
  longest = 0.0;
  smallestvolume = minaltitude;
  biggestvolume = 0.0;
  smallestratio = minaltitude;
  biggestratio = 0.0;
  smallestdiangle = smallestfaangle = 180.0;
  biggestdiangle = biggestfaangle = 0.0;

  // Loop all elements, calculate quality parameters for each element.
  tetrahedrons->traversalinit();
  tetloop.tet = tetrahedrontraverse();
  while (tetloop.tet != (tetrahedron *) NULL) {

    // Get four vertices: p0, p1, p2, p3.
    for (i = 0; i < 4; i++) p[i] = (point) tetloop.tet[4 + i];
    // Set the edge vectors: V[0], ..., V[5]
    for (i = 0; i < 3; i++) V[0][i] = p[0][i] - p[3][i]; // V[0]: p3->p0.
    for (i = 0; i < 3; i++) V[1][i] = p[1][i] - p[3][i]; // V[1]: p3->p1.
    for (i = 0; i < 3; i++) V[2][i] = p[2][i] - p[3][i]; // V[2]: p3->p2.
    for (i = 0; i < 3; i++) V[3][i] = p[1][i] - p[0][i]; // V[3]: p0->p1.
    for (i = 0; i < 3; i++) V[4][i] = p[2][i] - p[1][i]; // V[4]: p1->p2.
    for (i = 0; i < 3; i++) V[5][i] = p[0][i] - p[2][i]; // V[5]: p2->p0.
    // Set the matrix A = [V[0], V[1], V[2]]^T.
    for (j = 0; j < 3; j++) {
      for (i = 0; i < 3; i++) A[j][i] = V[j][i];
    }
    // Decompose A just once.
    lu_decmp(A, 3, indx, &D, 0);   
    // Get the tet volume.
    tetvol = fabs(A[indx[0]][0] * A[indx[1]][1] * A[indx[2]][2]) / 6.0;
    // Get the three faces normals.
    for (j = 0; j < 3; j++) {
      for (i = 0; i < 3; i++) rhs[i] = 0.0;
      rhs[j] = 1.0;  // Positive means the inside direction
      lu_solve(A, 3, indx, rhs, 0);
      for (i = 0; i < 3; i++) N[j][i] = rhs[i];
    }
    // Get the fourth face normal by summing up the first three.
    for (i = 0; i < 3; i++) N[3][i] = - N[0][i] - N[1][i] - N[2][i];
    // Get the radius of the circumsphere.
    for (i = 0; i < 3; i++) rhs[i] = 0.5 * dot(V[i], V[i]);
    lu_solve(A, 3, indx, rhs, 0);
    cirradius = sqrt(dot(rhs, rhs));
    // Normalize the face normals.
    for (i = 0; i < 4; i++) {
      // H[i] is the inverse of height of its corresponding face.
      H[i] = sqrt(dot(N[i], N[i]));
      for (j = 0; j < 3; j++) N[i][j] /= H[i];
    }
    // Get the radius of the inscribed sphere.
    // insradius = 1.0 / (H[0] + H[1] + H[2] + H[3]);
    // Get the biggest H[i] (corresponding to the smallest height).
    minheightinv = H[0];
    for (i = 1; i < 3; i++) {
      if (H[i] > minheightinv) minheightinv = H[i];
    }
    // Get the squares of the edge lengthes.
    for (i = 0; i < 6; i++) edgelength[i] = dot(V[i], V[i]);
    // Get the dihedrals (in degree) at each edges.
    j = 0;
    for (i = 1; i < 4; i++) {
      alldihed[j] = -dot(N[0], N[i]); // Edge cd, bd, bc.
      if (alldihed[j] < -1.0) alldihed[j] = -1; // Rounding.
      else if (alldihed[j] > 1.0) alldihed[j] = 1;
      alldihed[j] = acos(alldihed[j]) / PI * 180.0;
      j++;
    }
    for (i = 2; i < 4; i++) {
      alldihed[j] = -dot(N[1], N[i]); // Edge ad, ac.
      if (alldihed[j] < -1.0) alldihed[j] = -1; // Rounding.
      else if (alldihed[j] > 1.0) alldihed[j] = 1;
      alldihed[j] = acos(alldihed[j]) / PI * 180.0;
      j++;
    }
    alldihed[j] = -dot(N[2], N[3]); // Edge ab.
    if (alldihed[j] < -1.0) alldihed[j] = -1; // Rounding.
    else if (alldihed[j] > 1.0) alldihed[j] = 1;
    alldihed[j] = acos(alldihed[j]) / PI * 180.0;
    
    // Calculate the longest and shortest edge length.
    for (i = 0; i < 6; i++) {
      if (i == 0) {
        shortlen = longlen = edgelength[i];
      } else {
        shortlen = edgelength[i] < shortlen ? edgelength[i] : shortlen;
        longlen  = edgelength[i] > longlen  ? edgelength[i] : longlen;
      }
      if (edgelength[i] > longest) {
        longest = edgelength[i];
      } 
      if (edgelength[i] < shortest) {
        shortest = edgelength[i];
      }
    }
    
    // Calculate the largest and smallest volume.
    if (tetvol < smallestvolume) {
      smallestvolume = tetvol;
    } 
    if (tetvol > biggestvolume) {
      biggestvolume = tetvol;
    }
    
    // Calculate the largest and smallest dihedral angles.
    for (i = 0; i < 6; i++) {
      if (i == 0) {
        smalldiangle = bigdiangle = alldihed[i];
      } else {
        smalldiangle = alldihed[i] < smalldiangle ? alldihed[i] : smalldiangle;
        bigdiangle = alldihed[i] > bigdiangle ? alldihed[i] : bigdiangle;
      }
      if (alldihed[i] < smallestdiangle) {
        smallestdiangle = alldihed[i];
      } 
      if (alldihed[i] > biggestdiangle) {
        biggestdiangle = alldihed[i];
      }
    }
    // Accumulate the corresponding number in the dihedral angle histogram.
    if (smalldiangle < 5.0) {
      tendegree = 0;
    } else if (smalldiangle >= 5.0 && smalldiangle < 10.0) {
      tendegree = 1;
    } else if (smalldiangle >= 80.0 && smalldiangle < 110.0) {
      tendegree = 9; // Angles between 80 to 110 degree are in one entry.
    } else {
      tendegree = (int) (smalldiangle / 10.);
      if (smalldiangle < 80.0) {
        tendegree++;  // In the left column.
      } else {
        tendegree--;  // In the right column.
      }
    }
    dihedangletable[tendegree]++;
    if (bigdiangle >= 80.0 && bigdiangle < 110.0) {
      tendegree = 9; // Angles between 80 to 110 degree are in one entry.
    } else if (bigdiangle >= 170.0 && bigdiangle < 175.0) {
      tendegree = 16;
    } else if (bigdiangle >= 175.0) {
      tendegree = 17;
    } else {
      tendegree = (int) (bigdiangle / 10.);
      if (bigdiangle < 80.0) {
        tendegree++;  // In the left column.
      } else {
        tendegree--;  // In the right column.
      }
    }
    dihedangletable[tendegree]++;

    // Calulate the largest and smallest face angles.
    tetloop.ver = 0;
    for (tetloop.loc = 0; tetloop.loc < 4; tetloop.loc++) {
      sym(tetloop, neightet);
      // Only do the calulation once for a face.
      if ((neightet.tet == dummytet) || (tetloop.tet < neightet.tet)) {
        p[0] = org(tetloop);
        p[1] = dest(tetloop);
        p[2] = apex(tetloop);
        faceangle[0] = interiorangle(p[0], p[1], p[2], NULL);
        faceangle[1] = interiorangle(p[1], p[2], p[0], NULL);
        faceangle[2] = PI - (faceangle[0] + faceangle[1]);
        // Translate angles into degrees.
        for (i = 0; i < 3; i++) {
          faceangle[i] = (faceangle[i] * 180.0) / PI;
        }
        // Calculate the largest and smallest face angles.
        for (i = 0; i < 3; i++) {
          if (i == 0) {
            smallfaangle = bigfaangle = faceangle[i];
          } else {
            smallfaangle = faceangle[i] < smallfaangle ? 
              faceangle[i] : smallfaangle;
            bigfaangle = faceangle[i] > bigfaangle ? faceangle[i] : bigfaangle;
          }
          if (faceangle[i] < smallestfaangle) {
            smallestfaangle = faceangle[i];
          } 
          if (faceangle[i] > biggestfaangle) {
            biggestfaangle = faceangle[i];
          }
        }
        tendegree = (int) (smallfaangle / 10.);
        faceangletable[tendegree]++;
        tendegree = (int) (bigfaangle / 10.);
        faceangletable[tendegree]++;
      }
    }

    // Calculate aspect ratio and radius-edge ratio for this element.
    tetradius = cirradius / sqrt(shortlen);
    // tetaspect = sqrt(longlen) / (2.0 * insradius);
    tetaspect = sqrt(longlen) * minheightinv;
    // Remember the largest and smallest aspect ratio..
    if (tetaspect < smallestratio) {
      smallestratio = tetaspect;
    } 
    if (tetaspect > biggestratio) {
      biggestratio = tetaspect;
    }
    // Accumulate the corresponding number in the aspect ratio histogram.
    aspectindex = 0;
    while ((tetaspect > aspectratiotable[aspectindex]) && (aspectindex < 11)) {
      aspectindex++;
    }
    aspecttable[aspectindex]++;
    radiusindex = 0;
    while ((tetradius > radiusratiotable[radiusindex]) && (radiusindex < 11)) {
      radiusindex++;
    }
    radiustable[radiusindex]++;

    tetloop.tet = tetrahedrontraverse();
  }

  shortest = sqrt(shortest);
  longest = sqrt(longest);
  minaltitude = sqrt(minaltitude);

  //printf("  Smallest volume: %16.5g   |  Largest volume: %16.5g\n",
        // smallestvolume, biggestvolume);
  //printf("  Shortest edge:   %16.5g   |  Longest edge:   %16.5g\n",
         //shortest, longest);
  //printf("  Smallest aspect ratio:  %9.5g   |  Largest aspect ratio:  %9.5g\n",
        // smallestratio, biggestratio);
  sprintf(sbuf, "%.17g", biggestfaangle);
  if (strlen(sbuf) > 8) {
    sbuf[8] = '\0';
  }
  //printf("  Smallest facangle: %14.5g   |  Largest facangle:       %s\n",
         //smallestfaangle, sbuf);
  sprintf(sbuf, "%.17g", biggestdiangle);
  if (strlen(sbuf) > 8) {
    sbuf[8] = '\0';
  }
  //printf("  Smallest dihedral: %14.5g   |  Largest dihedral:       %s\n\n",
         //smallestdiangle, sbuf);

  /*
  //printf("  Radius-edge ratio histogram:\n");
  //printf("         < %-6.6g    :  %8d      | %6.6g - %-6.6g     :  %8d\n",
         radiusratiotable[0], radiustable[0], radiusratiotable[5],
         radiusratiotable[6], radiustable[6]);
  for (i = 1; i < 5; i++) {
    //printf("  %6.6g - %-6.6g    :  %8d      | %6.6g - %-6.6g     :  %8d\n",
           radiusratiotable[i - 1], radiusratiotable[i], radiustable[i],
           radiusratiotable[i + 5], radiusratiotable[i + 6],
           radiustable[i + 6]);
  }
  //printf("  %6.6g - %-6.6g    :  %8d      | %6.6g -            :  %8d\n",
         radiusratiotable[4], radiusratiotable[5], radiustable[5],
         radiusratiotable[10], radiustable[11]);
  //printf("  (A tetrahedron's radius-edge ratio is its radius of ");
  //printf("circumsphere divided\n");
  //printf("    by its shortest edge length)\n\n");
  */

  //printf("  Aspect ratio histogram:\n");
  //printf("         < %-6.6g    :  %8d      | %6.6g - %-6.6g     :  %8d\n",
         //aspectratiotable[0], aspecttable[0], aspectratiotable[5],
         //aspectratiotable[6], aspecttable[6]);
  for (i = 1; i < 5; i++) {
    //printf("  %6.6g - %-6.6g    :  %8d      | %6.6g - %-6.6g     :  %8d\n",
           //aspectratiotable[i - 1], aspectratiotable[i], aspecttable[i],
           //aspectratiotable[i + 5], aspectratiotable[i + 6],
          // aspecttable[i + 6]);
  }
  //printf("  %6.6g - %-6.6g    :  %8d      | %6.6g -            :  %8d\n",
         //aspectratiotable[4], aspectratiotable[5], aspecttable[5],
         //aspectratiotable[10], aspecttable[11]);
  //printf("  (A tetrahedron's aspect ratio is its longest edge length");
  //printf(" divided by its\n");
  //printf("    smallest side height)\n\n");

  //printf("  Face angle histogram:\n");
  for (i = 0; i < 9; i++) {
    //printf("    %3d - %3d degrees:  %8d      |    %3d - %3d degrees:  %8d\n",
           //i * 10, i * 10 + 10, faceangletable[i],
           //i * 10 + 90, i * 10 + 100, faceangletable[i + 9]);
  }
  if (minfaceang != PI) {
    //printf("  Minimum input face angle is %g (degree).\n",
           //minfaceang / PI * 180.0);
  }
  //printf("\n");

  //printf("  Dihedral angle histogram:\n");
  // Print the three two rows:
  //printf("     %3d - %2d degrees:  %8d      |    %3d - %3d degrees:  %8d\n",
         //0, 5, dihedangletable[0], 80, 110, dihedangletable[9]);
  //printf("     %3d - %2d degrees:  %8d      |    %3d - %3d degrees:  %8d\n",
         //5, 10, dihedangletable[1], 110, 120, dihedangletable[10]);
  // Print the third to seventh rows.
  for (i = 2; i < 7; i++) {
    //printf("     %3d - %2d degrees:  %8d      |    %3d - %3d degrees:  %8d\n",
           //(i - 1) * 10, (i - 1) * 10 + 10, dihedangletable[i],
           //(i - 1) * 10 + 110, (i - 1) * 10 + 120, dihedangletable[i + 9]);
  }
  // Print the last two rows.
  //printf("     %3d - %2d degrees:  %8d      |    %3d - %3d degrees:  %8d\n",
         //60, 70, dihedangletable[7], 170, 175, dihedangletable[16]);
  //printf("     %3d - %2d degrees:  %8d      |    %3d - %3d degrees:  %8d\n",
         //70, 80, dihedangletable[8], 175, 180, dihedangletable[17]);
  if (minfacetdihed != PI) {
    //printf("  Minimum input facet dihedral angle is %g (degree).\n",
           //minfacetdihed / PI * 180.0);
  }
  //printf("\n");
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// statistics()    Print all sorts of cool facts.                            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetgenmesh::statistics()
{
  //printf("\nStatistics:\n\n");
  //printf("  Input points: %d\n", in->numberofpoints + jettisoninverts);
  if (b->refine) {
    //printf("  Input tetrahedra: %d\n", in->numberoftetrahedra);
  }
  if (b->plc) {
    //printf("  Input facets: %d\n", in->numberoffacets);
    //printf("  Input segments: %ld\n", insegments);
    //printf("  Input holes: %d\n", in->numberofholes);
    //printf("  Input regions: %d\n", in->numberofregions);
  }

  //printf("\n  Mesh points: %ld\n", points->items);
  //printf("  Mesh tetrahedra: %ld\n", tetrahedrons->items);
  //printf("  Mesh faces: %ld\n", (4l * tetrahedrons->items + hullsize) / 2l);
  //printf("  Mesh edges: %ld\n", meshedges);

  if (b->plc || b->refine) {
    //printf("  Mesh boundary faces: %ld\n", subfaces->items);
    //printf("  Mesh boundary edges: %ld\n\n", subsegs->items);
  } else {
    //printf("  Convex hull faces: %ld\n\n", hullsize);
  }

  if (b->verbose > 0) {
    if (b->plc || b->refine) {
      qualitystatistics();
    }
    // algorithmicstatistics();
  }
}

////                                                                       ////
////                                                                       ////
//// report_cxx ///////////////////////////////////////////////////////////////

//// main_cxx /////////////////////////////////////////////////////////////////
////                                                                       ////
////                                                                       ////

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// tetrahedralize()    The interface for users using TetGen library to       //
//                     generate tetrahedral meshes with all features.        //
//                                                                           //
// The sequence is roughly as follows.  Many of these steps can be skipped,  //
// depending on the command line switches.                                   //
//                                                                           //
// - Initialize constants and parse the command line.                        //
// - Read the vertices from a file and either                                //
//   - tetrahedralize them (no -r), or                                       //
//   - read an old mesh from files and reconstruct it (-r).                  //
// - Insert the PLC segments and facets (-p).                                //
// - Read the holes (-p), regional attributes (-pA), and regional volume     //
//   constraints (-pa).  Carve the holes and concavities, and spread the     //
//   regional attributes and volume constraints.                             //
// - Enforce the constraints on minimum quality bound (-q) and maximum       //
//   volume (-a). Also enforce the conforming Delaunay property (-q and -a). //
// - Promote the mesh's linear tetrahedra to higher order elements (-o).     //
// - Write the output files and print the statistics.                        //
// - Check the consistency and Delaunay property of the mesh (-C).           //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

void tetrahedralize(tetgenbehavior *b, tetgenio *in, tetgenio *out,
  tetgenio *addin, tetgenio *bgmin)
{
  tetgenmesh m;
  // Variables for timing the performance of TetGen (defined in time.h).
  clock_t tv[17];

  tv[0] = clock();
 
  m.b = b;
  m.in = in;
  m.macheps = exactinit();
  m.steinerleft = b->steiner;
  if (b->metric) {
    m.bgm = new tetgenmesh();
    m.bgm->b = b;
    m.bgm->in = bgmin;
    m.bgm->macheps = exactinit();
  }
  m.initializepools();
  m.transfernodes();

  tv[1] = clock();

  if (b->refine) {
    m.reconstructmesh();
  } else {
    m.delaunizevertices();
    if (m.hullsize == 0l) {
      //printf("The input point set does not span a 3D subspace.\n");
      return;
    }
  }

  tv[2] = clock();

  if (!b->quiet) {
    if (b->refine) {
      //printf("Mesh reconstruction seconds:");
    } else {
      //printf("Delaunay seconds:");
    }
    //printf("  %g\n", (tv[2] - tv[1]) / (REAL) CLOCKS_PER_SEC);
  }

  if (b->metric) {
    if (bgmin != (tetgenio *) NULL) {
      m.bgm->initializepools();
      m.bgm->transfernodes();
      m.bgm->reconstructmesh();
    } else {
      m.bgm->in = in;
      m.bgm->initializepools();
      m.duplicatebgmesh();
    }
  }

  tv[3] = clock();

  if (!b->quiet) {
    if (b->metric) {
      //printf("Background mesh reconstruct seconds:  %g\n",
             //(tv[3] - tv[2]) / (REAL) CLOCKS_PER_SEC);
    }
  }

  if (b->useshelles && !b->refine) {
    m.meshsurface();
    tv[14] = clock();
    if (b->diagnose != 1) {
      m.markacutevertices(60.0);
      m.formskeleton(tv[15]);
    } else {
      m.detectinterfaces();
    }
  }

  tv[4] = clock();

  if (!b->quiet) {
    if (b->useshelles && !b->refine) {
      if (b->diagnose != 1) {
        //printf("Boundary recovery ");
      } else {
        //printf("Intersection "); 
      }
      //printf("seconds:  %g\n", (tv[4] - tv[3]) / (REAL) CLOCKS_PER_SEC);
      /*if ((b->diagnose != 1) && (b->verbose > 0)) {
        //printf("  Surface mesh seconds:  %g\n",
               (tv[14] - tv[3]) / (REAL) CLOCKS_PER_SEC);
        //printf("  Segment recovery seconds:  %g\n",
               (tv[15] - tv[14]) / (REAL) CLOCKS_PER_SEC);
        //printf("  Facet recovery seconds:  %g\n",
               (tv[4] - tv[15]) / (REAL) CLOCKS_PER_SEC);
      }*/
    }
  }

  if (b->plc && !(b->diagnose == 1)) {
    m.carveholes();
  }

  tv[5] = clock();

  if (!b->quiet) {
    if (b->plc && !(b->diagnose == 1)) {
      //printf("Hole seconds:  %g\n", (tv[5] - tv[4]) / (REAL) CLOCKS_PER_SEC);
    }
  }

  if ((b->plc || b->refine) && !(b->diagnose == 1)) {
    m.optimizemesh2(false); 
  }

  tv[6] = clock();

  if (!b->quiet) {
    if ((b->plc || b->refine) && !(b->diagnose == 1)) {
      //printf("Repair seconds:  %g\n", (tv[6] - tv[5]) / (REAL) CLOCKS_PER_SEC);
    }
  }

  if ((b->plc && b->nobisect) && !(b->diagnose == 1)) {
    m.removesteiners2();
  }

  tv[7] = clock();

  if (!b->quiet) {
    if ((b->plc && b->nobisect) && !(b->diagnose == 1)) {
      //printf("Steiner removal seconds:  %g\n",
            // (tv[7] - tv[6]) / (REAL) CLOCKS_PER_SEC);
    }
  }

  if (b->insertaddpoints && (addin != (tetgenio *) NULL)) {
    if (addin->numberofpoints > 0) {
      m.insertconstrainedpoints(addin); 
    }
  }

  tv[8] = clock();

  if (!b->quiet) {
    if ((b->plc || b->refine) && (b->insertaddpoints)) {
      //printf("Constrained points seconds:  %g\n", 
             //(tv[8] - tv[7]) / (REAL) CLOCKS_PER_SEC);
    }
  }

  if (b->metric) {
    m.interpolatesizemap();
  }

  tv[9] = clock();

  if (!b->quiet) {
    if (b->metric) {
      //printf("Size interpolating seconds:  %g\n",
             //(tv[9] - tv[8]) / (REAL) CLOCKS_PER_SEC);
    }
  }

  //if (b->coarse) {
  //  m.removesteiners2(true);
  //}

  tv[10] = clock();

  if (!b->quiet) {
    if (b->coarse) {
      //printf("Mesh coarsening seconds:  %g\n",
             //(tv[10] - tv[9]) / (REAL) CLOCKS_PER_SEC);
    }
  }

  if (b->quality) {
    m.enforcequality();
  }

  tv[11] = clock();

  if (!b->quiet) {
    if (b->quality) {
      //printf("Quality seconds:  %g\n",
             //(tv[11] - tv[10]) / (REAL) CLOCKS_PER_SEC);
    }
  }

  if (b->quality && (b->optlevel > 0)) {
    m.optimizemesh2(true);
  }

  tv[12] = clock();

  if (!b->quiet) {
    if (b->quality && (b->optlevel > 0)) {
      //printf("Optimize seconds:  %g\n",
             //(tv[12] - tv[11]) / (REAL) CLOCKS_PER_SEC);
    }
  }

  if (!b->nojettison && ((m.dupverts > 0) || (m.unuverts > 0)
      || (b->refine && (in->numberofcorners == 10)))) {
    m.jettisonnodes();
  }

  if (b->order > 1) {
    m.highorder();
  }

  if (!b->quiet) {
    //printf("\n");
  }

  if (out != (tetgenio *) NULL) {
    out->firstnumber = in->firstnumber;
    out->mesh_dim = in->mesh_dim;
  }

  if (b->nonodewritten || b->noiterationnum) {
    if (!b->quiet) {
      //printf("NOT writing a .node file.\n");
    }
  } else {
    if (b->diagnose == 1) {
      if (m.subfaces->items > 0l) {
        m.outnodes(out);  // Only output when self-intersecting faces exist.
      }
    } else {
      m.outnodes(out);
      if (b->metric) { //if (b->quality && b->metric) {
        m.outmetrics(out);
      }
    }
  }

  if (b->noelewritten == 1) {
    if (!b->quiet) {
      //printf("NOT writing an .ele file.\n");
    }
    m.numberedges();
  } else {
    if (!(b->diagnose == 1)) {
      if (m.tetrahedrons->items > 0l) {
        m.outelements(out);
      }
    }
  }

  if (b->nofacewritten) {
    if (!b->quiet) {
      //printf("NOT writing an .face file.\n");
    }
  } else {
    if (b->facesout) {
      if (m.tetrahedrons->items > 0l) {
        m.outfaces(out);  // Output all faces.
      }
    } else {
      if (b->diagnose == 1) {
        if (m.subfaces->items > 0l) {
          m.outsubfaces(out); // Only output self-intersecting faces.
        }
      } else if (b->plc || b->refine) {
        if (m.subfaces->items > 0l) {
          m.outsubfaces(out); // Output boundary faces.
        }
      } else {
        if (m.tetrahedrons->items > 0l) {
          m.outhullfaces(out); // Output convex hull faces.
        }
      }
    }
  }

  //if (m.checkpbcs) {
  //  m.outpbcnodes(out);
  //}

  if (b->edgesout) {
    if (b->edgesout > 1) {
      m.outedges(out); // -ee, output all mesh edges. 
    } else {
      m.outsubsegments(out); // -e, only output subsegments.
    }
  }

  if (!out && b->plc && 
      ((b->object == tetgenbehavior::OFF) ||
       (b->object == tetgenbehavior::PLY) ||
       (b->object == tetgenbehavior::STL))) {
    m.outsmesh(b->outfilename);
  }

  if (!out && b->meditview) {
    m.outmesh2medit(b->outfilename); 
  }

  if (!out && b->gidview) {
    m.outmesh2gid(b->outfilename); 
  }

  if (!out && b->geomview) {
    m.outmesh2off(b->outfilename); 
  }

  if (!out && b->vtkview) {
    m.outmesh2vtk(b->outfilename); 
  }

  if (b->neighout) {
    m.outneighbors(out);
  }

  if (b->voroout) {
    m.outvoronoi(out);
  }

  tv[13] = clock();

  if (!b->quiet) {
    //printf("\nOutput seconds:  %g\n",
           //(tv[13] - tv[12]) / (REAL) CLOCKS_PER_SEC);
    //printf("Total running seconds:  %g\n",
           //(tv[13] - tv[0]) / (REAL) CLOCKS_PER_SEC);
  }

  if (b->docheck) {
    m.checkmesh();
    if (m.checksubfaces) {
      m.checkshells();
    }
    if (b->docheck > 1) {
      if (m.checkdelaunay(0.0, NULL) > 0) {
        //assert(0);
      }
      if (b->docheck > 2) {
        if (b->quality || b->refine) {
          m.checkconforming();
        }
      }
    }
  }

  if (!b->quiet) {
    m.statistics();
  }

  if (b->metric) {
    delete m.bgm;
  }
}

#ifndef TETLIBRARY

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// main()    The entrance for running TetGen from command line.              //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

int main(int argc, char *argv[])

#else // with TETLIBRARY

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// tetrahedralize()    The entrance for calling TetGen from another program. //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

__device__ void tetrahedralize2(tetgenbehavior *b, tetgenio *in, tetgenio *out, tetgenmesh *m,
					tetgenio *addin, tetgenio *bgmin)
{
	// Variables for timing the performance of TetGen (defined in time.h).
	clock_t tv[17];

	tv[0] = clock();

	m->b = b;
	m->in = in;
	m->macheps = exactinit();
	m->steinerleft = b->steiner;

	//m->initializepools();
	m->transfernodes();

	//tv[1] = clock();


	m->delaunizevertices();
	if (m->hullsize == 0l) {
		////printf("The input point set does not span a 3D subspace.\n");
		return;
	}

	//tv[2] = clock();
	////printf("Delaunay seconds:");
	////printf("  %g\n", (tv[2] - tv[1]) / (REAL) CLOCKS_PER_SEC);

	if (out != (tetgenio *) NULL) {
		out->firstnumber = in->firstnumber;
		out->mesh_dim = in->mesh_dim;
	}

	m->outnodes(out);

	if (m->tetrahedrons->items > 0l) {
		m->outelements(out);
	}

	if (m->tetrahedrons->items > 0l) {
		m->outhullfaces(out); // Output convex hull faces.
	}

	/*if (!b->quiet) {
		m->statistics();
	}*/
}


__device__ void tetrahedralize(char *switches, tetgenio *in, tetgenio *out, tetgenmesh *m,
  tetgenio *addin, tetgenio *bgmin)

#endif // not TETLIBRARY

{
  tetgenbehavior b;

#ifndef TETLIBRARY

  tetgenio in, addin, bgmin;
  
  if (!b.parse_commandline(argc, argv)) {
    terminatetetgen(0);
  }
  if (b.refine) {
    if (!in.load_tetmesh(b.infilename)) {
      terminatetetgen(3);
    }
  } else {
    if (!in.load_plc(b.infilename, (int) b.object)) {
      terminatetetgen(3);
    }
  }
  if (b.insertaddpoints) {
    if (!addin.load_node(b.addinfilename)) {
      addin.numberofpoints = 0l;
    }
  }
  if (b.metric) {
    if (!bgmin.load_tetmesh(b.bgmeshfilename)) {
      bgmin.numberoftetrahedra = 0l;
    }
  }

  if (bgmin.numberoftetrahedra > 0l) {
    tetrahedralize(&b, &in, NULL, &addin, &bgmin);
  } else {
    tetrahedralize(&b, &in, NULL, &addin, NULL);
  }

  return 0;

#else // with TETLIBRARY

  //if (!b.parse_commandline(switches)) {
  //  //terminatetetgen(1);
	 // return;
  //}
  b.btree = 0;
  b.goodangle = 0.88302222155948906;
  tetrahedralize2(&b, in, out, m, addin, bgmin);

#endif // not TETLIBRARY
}

////                                                                       ////
////                                                                       ////
//// main_cxx /////////////////////////////////////////////////////////////////

